diff options
Diffstat (limited to 'Mailman/Cgi/admin.py')
| -rw-r--r-- | Mailman/Cgi/admin.py | 696 |
1 files changed, 374 insertions, 322 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py index 15179a994..9809940ee 100644 --- a/Mailman/Cgi/admin.py +++ b/Mailman/Cgi/admin.py @@ -30,201 +30,215 @@ from Mailman import Utils from Mailman import MailList from Mailman import Errors from Mailman import MailCommandHandler +from Mailman import i18n from Mailman.htmlformat import * from Mailman.Cgi import Auth from Mailman.Logging.Syslog import syslog -CATEGORIES = [] +# Mark, but don't translate yet +def _(s): return s + +CATEGORIES = [('general', _('General Options')), + ('members', _('Membership Management')), + ('privacy', _('Privacy Options')), + ('nondigest', _('Regular-member (non-digest) Options')), + ('digest', _('Digest-member Options')), + ('bounce', _('Bounce Options')), + ('archive', _('Archival Options')), + ('gateway', _('Mail-News and News-Mail gateways')), + ] + + +# Set up i18n +_ = i18n._ +i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) NL = '\n' def main(): - """Process and produce list options form. - - CGI input indicates that we're returning from submission of some new - settings, which is processed before producing the new version. - - """ global CATEGORIES - doc = Document() + + # Try to find out which list is being administered parts = Utils.GetPathPieces() if not parts: - FormatAdminOverview() + # None, so just do the admin overview and be done with it + admin_overview() return - # get the list object + # Get the list object listname = parts[0].lower() - try: - mlist = MailList.MailList(listname) + try: + mlist = MailList.MailList(listname, lock=0) except Errors.MMListError, e: - FormatAdminOverview(_('No such list <em>%s</em>') % listname) + admin_overview(_('No such list <em>%(listname)s</em>')) syslog('error', 'Someone tried to access the admin interface for a ' '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) try: - if len(parts) == 1: - category = 'general' - category_suffix = '' - else: - category = parts[1] - category_suffix = category - - # If the user is not authenticated, we're done. - cgidata = cgi.FieldStorage(keep_blank_values=1) - try: - Auth.authenticate(mlist, cgidata) - except Auth.NotLoggedInError, e: - Auth.loginpage(mlist, 'admin', e.message) - return - - # Is this a log-out request? - if category == 'logout': - print mlist.ZapCookie('admin') - Auth.loginpage(mlist, 'admin', frontpage=1) - return - - os.environ['LANG'] = mlist.preferred_language - - CATEGORIES = [('general', _("General Options")), - ('members', _("Membership Management")), - ('privacy', _("Privacy Options")), - ('nondigest', _("Regular-member (non-digest) Options")), - ('digest', _("Digest-member Options")), - ('bounce', _("Bounce Options")), - ('archive', _("Archival Options")), - ('gateway', _("Mail-News and News-Mail gateways")), - ] - - if category not in map(lambda x: x[0], CATEGORIES): - category = 'general' - - # is the request for variable details? - varhelp = None - if cgidata.has_key('VARHELP'): - varhelp = cgidata['VARHELP'].value - elif cgidata.has_key('request_login') and \ - os.environ.get('QUERY_STRING'): - # POST methods, even if their actions have a query string, don't - # get put into FieldStorage's keys :-( - qs = cgi.parse_qs(os.environ['QUERY_STRING']).get('VARHELP') - if qs and type(qs) == types.ListType: - varhelp = qs[0] - if varhelp: - FormatOptionHelp(doc, varhelp, mlist) - print doc.Format(bgcolor="#ffffff") - return - - # BAW: This doesn't appear to do anything. The pairs variable isn't - # used anywhere. Perhaps this was meant as some incomplete error - # checking on the value? - if cgidata.has_key('bounce_matching_headers'): - pairs = mlist.parse_matching_header_opt() - - if len(cgidata.keys()): - ChangeOptions(mlist, category, cgidata, doc) + Auth.authenticate(mlist, cgidata) + except Auth.NotLoggedInError, e: + Auth.loginpage(mlist, 'admin', e.message) + return + # Which subcategory was requested? Default is `general' + if len(parts) == 1: + category = 'general' + category_suffix = '' + else: + category = parts[1] + category_suffix = category + # Is this a log-out request? + if category == 'logout': + print mlist.ZapCookie('admin') + Auth.loginpage(mlist, 'admin', frontpage=1) + return + # Sanity check + if category not in [x[0] for x in CATEGORIES]: + category = 'general' + # Is the request for variable details? + varhelp = None + if cgidata.has_key('VARHELP'): + varhelp = cgidata['VARHELP'].value + elif cgidata.has_key('request_login') and os.environ.get('QUERY_STRING'): + # POST methods, even if their actions have a query string, don't get + # put into FieldStorage's keys :-( + qs = cgi.parse_qs(os.environ['QUERY_STRING']).get('VARHELP') + if qs and type(qs) == types.ListType: + varhelp = qs[0] + if varhelp: + option_help(mlist, varhelp) + return + # The html page document + doc = Document() + doc.set_language(mlist.preferred_language) + # Now we're ready to do normal form processing. For this, though we must + # lock the mailing list, and everything from here on out must be wrapped + # in a try/except. + # + # BAW: Currently we attempt to acquire the lock with no timeout, although + # this could get hit by a webserver or client timeout, if there's a long + # or stale lock on the list. Maybe we should have a configurable timeout + # setting after which we'll just inform the user that the operation + # couldn't be performed? + mlist.Lock() + try: + if cgidata.keys(): + # There are options to change + change_options(mlist, category, cgidata, doc) + # Let the list sanity check the changed values mlist.CheckValues() - - # Sanity checks + # Additional sanity checks if not mlist.digestable and not mlist.nondigestable: - AddErrorMessage(doc, _('''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.''')) + add_error_message( + doc, + _('''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.''')) + if not mlist.digestable and len(mlist.GetDigestMembers()): - AddErrorMessage(doc, _('''You have digest members, - but digests are turned off. Those people will not receive - mail.''')) + add_error_message( + doc, + _('''You have digest members, but digests are turned + off. Those people will not receive mail.''')) if not mlist.nondigestable and len(mlist.GetMembers()): - AddErrorMessage(doc, _('''You have regular list members - but non-digestified mail is turned off. They will receive mail - until you fix this problem.''')) - - FormatConfiguration(doc, mlist, category, category_suffix, cgidata) - print doc.Format(bgcolor="#ffffff") + add_error_message( + doc, + _('''You have regular list members but non-digestified mail is + turned off. They will receive mail until you fix this + problem.''')) + # Glom up the results page and print it out + show_results(mlist, doc, category, category_suffix, cgidata) + print doc.Format(bgcolor='#ffffff') finally: mlist.Save() mlist.Unlock() -# Form Production: -def FormatAdminOverview(error=None): - "Present a general welcome and itemize the (public) lists." - doc = Document() - default_hostname = mm_cfg.DEFAULT_HOST_NAME +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 = mm_cfg.DEFAULT_HOST_NAME legend = _('%(default_hostname)s mailing lists - Admin Links') + # The html `document' + doc = Document() + doc.set_language(mm_cfg.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(max(table.GetCurrentRowIndex(), 0), 0, colspan=2, bgcolor="#99ccff") - + # Skip any mailing list that isn't advertised. advertised = [] - names = Utils.list_names() - names.sort() - for n in names: - l = MailList.MailList(n, lock=0) - if l.advertised: - advertised.append(l) - - os.environ['LANG'] = mm_cfg.DEFAULT_SERVER_LANGUAGE - - if error: - greeting = FontAttr(error, color="ff5060", size="+1") + listnames = Utils.list_names() + listnames.sort() + for name in listnames: + mlist = MailList.MailList(name, lock=0) + if mlist.advertised: + advertised.append(mlist) + # Greeting depends on whether there was an error or not + if msg: + greeting = FontAttr(msg, color="ff5060", size="+1") else: greeting = _("Welcome!") + welcome = [] if not advertised: - welcome_items = (greeting, - _("<p>" - " There currently are no publicly-advertised "), - Link(mm_cfg.MAILMAN_URL, "mailman"), - _(" mailing lists on %s.") % mm_cfg.DEFAULT_HOST_NAME, - ) + welcome.extend([ + greeting, + _('<p>There currently are no publicly-advertised '), + Link(mm_cfg.MAILMAN_URL, _('Mailman')), + _(' mailing lists on %(hostname)s.'), + ]) else: - welcome_items = ( + welcome.extend([ greeting, - _("<p>" - " Below is the collection of publicly-advertised "), - Link(mm_cfg.MAILMAN_URL, "mailman"), - _(" mailing lists on %(default_hostname)s."), - (_(' Click on a list name to visit the configuration pages' - ' for that list.') - ) - ) + _('<p>Below is the collection of publicly-advertised '), + Link(mm_cfg.MAILMAN_URL, _('Mailman')), + _(' mailing lists on %(hostname)s.'), + _(''' Click on a list name to visit the configuration pages for + that list.'''), + ]) mailman_owner = mm_cfg.MAILMAN_OWNER - extra = error and _('right ') or '' - welcome_items = (welcome_items + - (_(" 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.<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:%(mailman_owner)s', mailman_owner), - ".)<p>" - ) - ) + 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. - table.AddRow([apply(Container, welcome_items)]) + <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:%(mailman_owner)s', mailman_owner), + '.)<p>', + ]) + + table.addRow([Container(*welcome)]) table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2) if advertised: table.AddRow([' ', ' ']) - table.AddRow([Bold(_("List")), Bold(_("Description"))]) - for l in advertised: + table.AddRow([Bold(_('List')), Bold(_('Description'))]) + for mlist in advertised: table.AddRow( - [Link(l.GetScriptURL('admin'), Bold(l.real_name)), - l.description or Italic(_('[no description available]')), + [Link(mlist.GetScriptURL('admin'), Bold(mlist.real_name)), + mlist.description or Italic(_('[no description available]')), ]) doc.AddItem(table) @@ -234,24 +248,91 @@ def FormatAdminOverview(error=None): -def FormatConfiguration(doc, mlist, category, category_suffix, cgi_data): - """Produce the overall doc, *except* any processing error messages.""" +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 = varref.split('/') + if len(reflist) == 2: + category, varname = reflist + options = get_config_options(mlist, category) + 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: + path_info = os.environ.get('PATH_INFO') + bad = _('No valid variable details request not found: %(path_info)s') + add_error_message(doc, bad) + print doc.Format(bgcolor='#fffff') + return + # Get the details about the variable + varname, kind, params, dependancies, description, elaboration = \ + get_item_characteristics(item) + if elaboration is None: + elaboration = desc + # + # 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(max(header.GetCurrentRowIndex(), 0), 0, + colspan=2, bgcolor="#99ccff") + doc.SetTitle(_("Mailman %(varname)s List Option Help")) + doc.AddItem(header) + doc.AddItem("<b>%s</b> (%s): %s<p>" % (varname, category, descr)) + doc.AddItem("%s<p>" % elaboration) + + form = Form("%s/%s" % (mlist.GetScriptURL('admin'), category)) + valtab = Table(cellspacing=3, cellpadding=4) + add_options_table_item(mlist, category, 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 + """)) + + doc.AddItem(Link('%s/%s' % (mlist.GetScriptURL('admin'), category), + _('return to the %(category)s options page.'))) + doc.AddItem('</em>') + doc.AddItem(mlist.GetMailmanFooter()) + print doc.Format(bgcolor="#ffffff") + + + +def show_results(mlist, doc, category, category_suffix, cgidata): + # Produce the results page + global CATEGORIES + + adminurl = mlist.GetScriptURL('admin') + for k, v in CATEGORIES: if k == category: - label = v + label = _(v) + break + # 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>') - - linktable = Table(valign="top") + # This holds the two columns of links + linktable = Table(valign='top') linktable.AddRow([Center(Bold(_("Configuration Categories"))), Center(Bold(_("Other Administrative Activities")))]) - adminurl = mlist.GetScriptURL('admin') - + # The `other links' are stuff in the right column. otherlinks = UnorderedList() otherlinks.AddItem(Link(mlist.GetScriptURL('admindb'), _('Tend to pending administrative requests'))) @@ -259,79 +340,99 @@ def FormatConfiguration(doc, mlist, category, category_suffix, cgi_data): _('Go to the general list information page'))) otherlinks.AddItem(Link(mlist.GetScriptURL('edithtml'), _('Edit the HTML for the public list pages'))) - otherlinks.AddItem(Link(mlist.GetBaseArchiveURL(), 'Go to list archives')) + otherlinks.AddItem(Link(mlist.GetBaseArchiveURL(), + _('Go to list archives'))) otherlinks.AddItem(Link('%s/logout' % adminurl, - # TBD: What I really want is a blank line :/ - '<FONT SIZE="+2"><b>Logout</b></FONT>')) - + # 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 = UnorderedList() for k, v in CATEGORIES: + url = '%s/%s' % (adminurl, k) + # Translate + v = _(v) if k == category: - categorylinks.AddItem("<em>%s</em>" % v) - else: - categorylinks.AddItem(Link("%s/%s" % (adminurl, k), v)) - + # BAW: Is there a better UI for the category we're currently + # displaying? I'd like a non-underlined, non-colored, italicized, + # but live link. + v = '<em><font size="+2">%s</font></em>' % v + categorylinks.AddItem(Link("%s/%s" % (adminurl, k), v)) + # Add all the links to the links table... linktable.AddRow([categorylinks, otherlinks]) linktable.AddRowInfo(max(linktable.GetCurrentRowIndex(), 0), - valign="top") - + valign='top') + # ...and add the links table to the document. doc.AddItem(linktable) 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' category supports file uploads. if category_suffix: encoding = None if category_suffix == 'autoreply': - # these have file uploads + # These have file uploads encoding = 'multipart/form-data' form = Form('%s/%s' % (adminurl, category_suffix), encoding=encoding) else: form = Form(adminurl) + # And add the form doc.AddItem(form) - + # The general category supports changing the password. if category == 'general': - andpassmsg = _(" (You can change your password there, too.)") + andpassmsg = _(' (You can change your password there, too.)') else: - andpassmsg = "" - form.AddItem(_("Make your changes below, and then submit them" - " using the button at the bottom.") + - andpassmsg + '<p>') + andpassmsg = '' + form.AddItem( + _('''Make your changes below, and then submit them + using the button at the bottom.''') + + andpassmsg + + '<p>') - form.AddItem(FormatOptionsSection(category, mlist, cgi_data)) + form.AddItem(show_variables(mlist, category, cgidata)) if category == 'general': form.AddItem(Center(FormatPasswordStuff())) form.AddItem("<p>") - form.AddItem(Center(FormatSubmit())) + form.AddItem(Center(submit_button())) form.AddItem(mlist.GetMailmanFooter()) + # main() formats and prints the document -def FormatOptionsSection(category, mlist, cgi_data): - """Produce the category-specific options table.""" +def show_variables(mlist, category, cgidata): + # Produce the category specific variable options table if category == 'members': # Special case for members section. - return FormatMembershipOptions(mlist, cgi_data) - - options = GetConfigOptions(mlist, category) + return membership_options(mlist, cgidata) - big_table = Table(cellspacing=3, cellpadding=4) + options = get_config_options(mlist, category) + # The table containing the results + table = Table(cellspacing=3, cellpadding=4) # Get and portray the text label for the category. for k, v in CATEGORIES: if k == category: - label = v - big_table.AddRow([Center(Header(2, label))]) - big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 0, + label = _(v) + break + + table.AddRow([Center(Header(2, label))]) + table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2, bgcolor="#99ccff") - def ColHeader(big_table = big_table): - big_table.AddRow([Center(Bold(_('Description'))), Center(Bold(_('Value')))]) - big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 0, - width="15%") - big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 1, - width="85%") - did_col_header = 0 + # Convenience + def column_header(table=table): + 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%') + did_col_header = 0 for item in options: if type(item) == types.StringType: # The very first banner option (string in an options list) is @@ -339,35 +440,33 @@ def FormatOptionsSection(category, mlist, cgi_data): # treated as section headers - centered and italicized... if did_col_header: item = "<center><i>" + item + "</i></center>" - big_table.AddRow([item]) - big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), + table.AddRow([item]) + table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2) if not did_col_header: # Do col header after very first string descr, if any... - ColHeader() + column_header() did_col_header = 1 else: if not did_col_header: # ... but do col header before anything else. - ColHeader() + column_header() did_col_header = 1 - AddOptionsTableItem(big_table, item, category, mlist) - big_table.AddRow(['<br>']) - big_table.AddCellInfo(big_table.GetCurrentRowIndex(), 0, colspan=2) - return big_table + add_options_table_item(mlist, category, table, item) + table.AddRow(['<br>']) + table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) + return table -def AddOptionsTableItem(table, item, category, mlist, detailsp=1): - """Add a row to an options table with the item description and value.""" - try: - got = GetItemCharacteristics(item) - varname, kind, params, dependancies, descr, elaboration = got - except ValueError, msg: - syslog('error', 'admin: %s' % msg) - return Italic(_("<malformed option>")) - descr = GetItemGuiDescr(mlist, category, varname, descr, detailsp) - val = GetItemGuiValue(mlist, kind, varname, params) +def add_options_table_item(mlist, category, table, item, detailsp=1): + # Add a row to an options table with the item description and value. + varname, kind, params, dependancies, descr, elaboration = \ + get_item_characteristics(item) + if elaboration is None: + elaboration = descr + descr = get_item_gui_description(mlist, category, varname, descr, detailsp) + val = get_item_gui_value(mlist, kind, varname, params) table.AddRow([descr, val]) table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 1, bgcolor="#cccccc") @@ -376,80 +475,28 @@ def AddOptionsTableItem(table, item, category, mlist, detailsp=1): -def FormatOptionHelp(doc, varref, mlist): - item = None - reflist = varref.split('/') - if len(reflist) == 2: - category, varname = reflist - options = GetConfigOptions(mlist, category) - for i in options: - if i and i[0] == varname: - item = i - break - if not item: - bad = _('Option %s/%s not found: %s') % ( - category, varname, os.environ.get('PATH_INFO')) - AddErrorMessage(doc, bad) - return - got = GetItemCharacteristics(item) - try: - varname, kind, params, dependancies, descr, elaboration = got - if elaboration is None: - elaboration = '' - except ValueError, msg: - varname, kind, params, dependancies, descr = got - elaboration = descr - header = Table(width="100%") - realname = mlist.real_name - legend = (_("""%(realname)s Mailing list Configuration Help - <br><em>%(varname)s</em> Option""")) - header.AddRow([Center(Header(3, legend))]) - header.AddCellInfo(max(header.GetCurrentRowIndex(), 0), 0, - colspan=2, bgcolor="#99ccff") - doc.SetTitle(_("Mailman %(varname)s List Option Help")) - doc.AddItem(header) - doc.AddItem("<b>%s</b> (%s): %s<p>" % (varname, category, descr)) - doc.AddItem("%s<p>" % elaboration) - - form = Form("%s/%s" % (mlist.GetScriptURL('admin'), category)) - valtab = Table(cellspacing=3, cellpadding=4) - AddOptionsTableItem(valtab, item, category, mlist, detailsp=0) - form.AddItem(valtab) - form.AddItem('<p>') - form.AddItem(Center(FormatSubmit())) - 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 """)) - doc.AddItem(Link('%s/%s' % (mlist.GetScriptURL('admin'), category), - _('return to the %(category)s options page.'))) - doc.AddItem('</em>') - doc.AddItem(mlist.GetMailmanFooter()) - - - -def GetItemCharacteristics(table_entry): - """Break out the components of an item description from its table entry: - 0 option-var name - 1 type - 2 entry size - 3 ?dependancies? - 4 Brief description - 5 Optional description elaboration""" - if len(table_entry) == 5: +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 = table_entry - elif len(table_entry) == 6: - varname, kind, params, dependancies, descr, elaboration = table_entry + 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 %s") - % table_entry) - return (varname, kind, params, dependancies, descr, elaboration) + raise ValueError, _('Badly formed options entry:\n %(record)s') + return varname, kind, params, dependancies, descr, elaboration -def GetItemGuiValue(mlist, kind, varname, params): +def get_item_gui_value(mlist, kind, varname, params): """Return a representation of an item's settings.""" if kind == mm_cfg.Radio or kind == mm_cfg.Toggle: # @@ -502,30 +549,28 @@ def GetItemGuiValue(mlist, kind, varname, params): container.AddItem(_('<br><em>...specify a file to upload</em><br>')) container.AddItem(FileUpload(varname+'_upload', r, c)) return container - - # jcrey - new to deal with language + # jcrey - new to deal with language popup elif kind == mm_cfg.Select: if params: values, legend, selected = params else: - values = mlist.GetAvailableLanguages() - legend = map(_, map(Utils.GetLanguageDescr, values)) + values = mlist.GetAvailableLanguages() + legend = map(_, map(Utils.GetLanguageDescr, values)) selected = values.index(mlist.preferred_language) return SelectOptions(varname, values, legend, selected) -def GetItemGuiDescr(mlist, category, varname, descr, 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! - """ +def get_item_gui_description(mlist, category, varname, descr, 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: text = Container('<div ALIGN="right">' + descr + ' ', Link(mlist.GetScriptURL('admin') + '/?VARHELP=' + category + '/' + varname, - '(Details)'), + _('(Details)')), '</div>').Format() else: text = '<div ALIGN="right">' + descr + '</div>' @@ -537,7 +582,7 @@ def GetItemGuiDescr(mlist, category, varname, descr, detailsp): -def FormatMembershipOptions(mlist, cgi_data): +def membership_options(mlist, cgidata): container = Container() header = Table(width="100%") header.AddRow([Center(Header(2, _("Membership Management")))]) @@ -560,7 +605,8 @@ def FormatMembershipOptions(mlist, cgi_data): bgcolor="#cccccc", colspan=9) user_table.AddRow(map(Center, [_('member address'), _('subscr'), - _('hide'), _('nomail'), _('ack'), _('not metoo'), + _('hide'), _('nomail'), _('ack'), + _('not metoo'), _('digest'), _('plain'), _('language')])) rowindex = user_table.GetCurrentRowIndex() for i in range(9): @@ -568,21 +614,23 @@ def FormatMembershipOptions(mlist, cgi_data): all = mlist.GetMembers() + mlist.GetDigestMembers() if len(all) > mlist.admin_member_chunksize: chunks = Utils.chunkify(all, mlist.admin_member_chunksize) - if not cgi_data.has_key("chunk"): + if not cgidata.has_key("chunk"): chunk = 0 else: - chunk = int(cgi_data["chunk"].value) + chunk = int(cgidata["chunk"].value) all = chunks[chunk] - footer = (_("<p><em>To View other sections, " - "click on the appropriate range listed below</em>")) + + footer = _('''<p><em>To View other sections, click on the appropriate + range listed below</em>''') + chunk_indices = range(len(chunks)) chunk_indices.remove(chunk) buttons = [] for ci in chunk_indices: start, end = chunks[ci][0], chunks[ci][-1] url = mlist.GetScriptURL('admin') - buttons.append("<a href=%s/members?chunk=%d>" + _("from %s to %s") + "</a>" - % (url, ci, start, end)) + buttons.append("<a href=%(url)s/members?chunk=%(ci)d>" + + _("from %(start)s to %(end)s") + "</a>") buttons = apply(UnorderedList, tuple(buttons)) footer = footer + buttons.Format() + "<p>" else: @@ -631,22 +679,23 @@ def FormatMembershipOptions(mlist, cgi_data): container.AddItem(Center(user_table)) legend = UnorderedList() legend.AddItem(_('<b>subscr</b> -- Is the member subscribed?')) - legend.AddItem(_("<b>hide</b> -- Is the member's address " - "concealed on the list of subscribers?")) + 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?')) - legend.AddItem(_('<b>ack</b> -- ' - 'Does the member get acknowledgements of their posts?')) - legend.AddItem(_('<b>not metoo</b> -- ' - 'Does the member avoid copies of their own posts?')) - 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")) + _('''<b>ack</b> -- Does the member get acknowledgements of their + posts?''')) + legend.AddItem( + _('''<b>not metoo</b> -- Does the member avoid copies of their own + posts?''')) + 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")) container.AddItem(legend.Format()) container.AddItem(footer) t = Table(width="90%") @@ -684,7 +733,9 @@ def FormatPasswordStuff(): change_pw_table.AddCellInfo(0, 0, align="left", colspan=2) old = Table(bgcolor="#99cccc", border=1, cellspacing=0, cellpadding=2, valign="top") - old.AddRow(['<div ALIGN="right">' + _(" Enter current password:") + '</div>', + old.AddRow(['<div ALIGN="right">' + + _(" Enter current password:") + + '</div>', PasswordBox('adminpw')]) new = Table(bgcolor="#99cccc", border=1, cellspacing=0, cellpadding=2, valign="top") @@ -699,7 +750,7 @@ def FormatPasswordStuff(): -def FormatSubmit(): +def submit_button(): submit = Table(bgcolor="#99ccff", border=0, cellspacing=0, cellpadding=2) submit.AddRow([Bold(SubmitButton('submit', _('Submit Your Changes')))]) @@ -708,11 +759,8 @@ def FormatSubmit(): -# XXX klm - looks like turn_on_moderation is orphaned. -#turn_on_moderation = 0 - # Options processing -def GetValidValue(mlist, prop, my_type, val, dependant): +def get_valid_value(mlist, prop, my_type, val, dependant): if my_type == mm_cfg.Radio or my_type == mm_cfg.Toggle: if type(val) <> types.IntType: try: @@ -766,7 +814,7 @@ def GetValidValue(mlist, prop, my_type, val, dependant): -def ChangeOptions(mlist, category, cgi_info, document): +def change_options(mlist, category, cgi_info, document): confirmed = 0 if cgi_info.has_key('newpw'): if cgi_info.has_key('confirmpw'): @@ -775,14 +823,14 @@ def ChangeOptions(mlist, category, cgi_info, document): mlist.ConfirmAdminPassword(cgi_info['adminpw'].value) confirmed = 1 except Errors.MMBadPasswordError: - AddErrorMessage(document, + add_error_message(document, _('Incorrect administrator password'), tag='Error: ') if confirmed: new = cgi_info['newpw'].value.strip() confirm = cgi_info['confirmpw'].value.strip() if new == '' and confirm == '': - AddErrorMessage(document, + add_error_message(document, _('Empty admin passwords are not allowed'), tag='Error: ') elif new == confirm: @@ -790,10 +838,10 @@ def ChangeOptions(mlist, category, cgi_info, document): # Re-authenticate (to set new cookie) mlist.WebAuthenticate(password=new, cookie='admin') else: - AddErrorMessage(document, _('Passwords did not match'), + add_error_message(document, _('Passwords did not match'), tag='Error: ') else: - AddErrorMessage(document, + add_error_message(document, _('You must type in your new password twice'), tag='Error: ') # @@ -814,7 +862,7 @@ def ChangeOptions(mlist, category, cgi_info, document): # page_setting = int(cgi_info["subscribe_policy"].value) cgi_info["subscribe_policy"].value = str(page_setting + 1) - opt_list = GetConfigOptions(mlist, category) + opt_list = get_config_options(mlist, category) for item in opt_list: if type(item) <> types.TupleType or len(item) < 5: continue @@ -826,7 +874,7 @@ def ChangeOptions(mlist, category, cgi_info, document): continue else: val = cgi_info[property].value - value = GetValidValue(mlist, property, kind, val, deps) + value = get_valid_value(mlist, property, kind, val, deps) # # This is an ugly, ugly hack if property[0] == '_': @@ -849,6 +897,10 @@ def ChangeOptions(mlist, category, cgi_info, document): changed! It must differ from the list's name by case only.<p>""")) continue + # Watch for changes to preferred_language. If found, make + # sure that the response is generated in the new language. + if property == 'preferred_language': + i18n.set_language(value) setattr(mlist, property, value) # # mass subscription processing for members category @@ -936,18 +988,18 @@ def ChangeOptions(mlist, category, cgi_info, document): mlist.SetUserOption(user, opt_code, 0, save_list=0) if errors: document.AddItem(Header(5, _("Error Unsubscribing:"))) - items = map(lambda x: "%s -- %s" % (x[0], x[1]), errors) + items = ['%s -- %s' % (x[0], x[1]) for x in errors] document.AddItem(apply(UnorderedList, tuple((items)))) document.AddItem("<p>") -def AddErrorMessage(doc, errmsg, tag='Warning: ', *args): +def add_error_message(doc, errmsg, tag='Warning: ', *args): doc.AddItem(Header(3, Bold(FontAttr( _(tag), color="#ff0000", size="+2")).Format() + Italic(errmsg % args).Format())) -def GetConfigOptions(mlist, category): +def get_config_options(mlist, category): return mlist.GetConfigInfo()[category] |
