diff options
| author | bwarsaw | 2001-07-19 01:33:38 +0000 |
|---|---|---|
| committer | bwarsaw | 2001-07-19 01:33:38 +0000 |
| commit | 5e4dcec7406cd091fdd94e0d2d105c6be76a8b6b (patch) | |
| tree | 4920df5a7739f6df86960843ce84b681f9bd6b19 /Mailman/Cgi/admin.py | |
| parent | 81e4d9dc47929c1684f67412b085e8f8a9685a83 (diff) | |
| download | mailman-5e4dcec7406cd091fdd94e0d2d105c6be76a8b6b.tar.gz mailman-5e4dcec7406cd091fdd94e0d2d105c6be76a8b6b.tar.zst mailman-5e4dcec7406cd091fdd94e0d2d105c6be76a8b6b.zip | |
Diffstat (limited to 'Mailman/Cgi/admin.py')
| -rw-r--r-- | Mailman/Cgi/admin.py | 277 |
1 files changed, 164 insertions, 113 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py index 5036eccdc..bb3ad7e14 100644 --- a/Mailman/Cgi/admin.py +++ b/Mailman/Cgi/admin.py @@ -40,21 +40,6 @@ from Mailman.htmlformat import * from Mailman.Cgi import Auth from Mailman.Logging.Syslog import syslog -# Mark, but don't translate yet -def _(s): return s - -CATEGORIES = [('general', _('General Options')), - ('members', _('Membership Management')), - ('privacy', _('Privacy Options')), - ('nondigest', _('Regular delivery (non-digest) Options')), - ('digest', _('Digest Options')), - ('bounce', _('Bounce Options')), - ('archive', _('Archival Options')), - ('gateway', _('Mail-News and News-Mail gateways')), - ('autoreply', _('Auto-responder')), - ] - - # Set up i18n _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) @@ -103,14 +88,17 @@ def main(): else: category = parts[1] category_suffix = category + # Is this a log-out request? if category == 'logout': print mlist.ZapCookie(mm_cfg.AuthListAdmin) Auth.loginpage(mlist, 'admin', frontpage=1) return + # Sanity check - if category not in [x[0] for x in CATEGORIES]: + if category not in mlist.GetConfigCategories().keys(): category = 'general' + # Is the request for variable details? varhelp = None if cgidata.has_key('VARHELP'): @@ -124,27 +112,28 @@ def main(): if varhelp: option_help(mlist, varhelp) return + # The html page document doc = Document() doc.set_language(mlist.preferred_language) # From this point on, the MailList object must be locked. However, we - # must release the lock no matter how we exit. try/finally isn't - # enough, because of this scenario: user hits the admin page which may - # take a long time to render; user gets bored and hits the browser's - # STOP button; browser shuts down socket; server tries to write to - # broken socket and gets a SIGPIPE. Under Apache 1.3/mod_cgi, Apache - # catches this SIGPIPE (I presume it is buffering output from the cgi - # script), then turns around and SIGTERMs the cgi process. Apache - # waits three seconds and then SIGKILLs the cgi process. We /must/ - # catch the SIGTERM and do the most reasonable thing we can in as - # short a time period as possible. If we get the SIGKILL we're - # screwed (because its uncatchable and we'll have no opportunity to - # clean up after ourselves). + # must release the lock no matter how we exit. try/finally isn't enough, + # because of this scenario: user hits the admin page which may take a long + # time to render; user gets bored and hits the browser's STOP button; + # browser shuts down socket; server tries to write to broken socket and + # gets a SIGPIPE. Under Apache 1.3/mod_cgi, Apache catches this SIGPIPE + # (I presume it is buffering output from the cgi script), then turns + # around and SIGTERMs the cgi process. Apache waits three seconds and + # then SIGKILLs the cgi process. We /must/ catch the SIGTERM and do the + # most reasonable thing we can in as short a time period as possible. If + # we get the SIGKILL we're screwed (because it's uncatchable and we'll + # have no opportunity to clean up after ourselves). # - # This signal handler catches the SIGTERM and unlocks the list. The - # effect of this is that the changes made to the MailList object will - # be aborted, which seems like the only sensible semantics. + # This signal handler catches the SIGTERM, unlocks the list, and then + # exits the process. The effect of this is that the changes made to the + # MailList object will be aborted, which seems like the only sensible + # semantics. # # BAW: This may not be portable to other web servers or cgi execution # models. @@ -176,12 +165,12 @@ def main(): non-digest delivery or your mailing list will basically be unusable.''')) - if not mlist.digestable and len(mlist.GetDigestMembers()): + if not mlist.digestable and mlist.getDigestMemberKeys(): 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()): + if not mlist.nondigestable and mlist.getRegularMemberKeys(): add_error_message( doc, _('''You have regular list members but non-digestified mail is @@ -300,7 +289,7 @@ def option_help(mlist, varhelp): reflist = varhelp.split('/') if len(reflist) == 2: category, varname = reflist - options = get_config_options(mlist, category) + options = mlist.GetConfigInfo()[category] for i in options: if i and i[0] == varname: item = i @@ -356,11 +345,8 @@ def option_help(mlist, varhelp): def show_results(mlist, doc, category, category_suffix, cgidata): # Produce the results page adminurl = mlist.GetScriptURL('admin') - - for k, v in CATEGORIES: - if k == category: - label = _(v) - break + categories = mlist.GetConfigCategories() + label = _(categories[category][0]) # Set up the document's headers realname = mlist.real_name @@ -396,28 +382,32 @@ def show_results(mlist, doc, category, category_suffix, cgidata): _('Logout'))) # These are links to other categories and live in the left column categorylinks = UnorderedList() - for k, v in CATEGORIES: + for k in categories.keys(): + label = _(categories[k][0]) url = '%s/%s' % (adminurl, k) - # Translate - v = _(v) if k == category: - # Membership management has some subcategories - if k == category == 'members': - subcat_items = [] + # Handle subcategories + subcats = mlist.GetConfigSubCategories(k) + if subcats: subcat = Utils.GetPathPieces()[-1] - if subcat == 'members': - subcat = 'list' - for sub, text in (('list', _('Membership List')), - ('add', _('Mass Subscription')), - ('remove', _('Mass Removal'))): + 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('%s/%s/%s' % (adminurl, k, sub), - text)) - v += UnorderedList(*subcat_items).Format() + subcat_items.append(Link(url + '/' + sub, text)) + categorylinks.AddItem( + Bold(label).Format() + + UnorderedList(*subcat_items).Format()) else: - v = Bold('[%s]' % v).Format() - categorylinks.AddItem(Link('%s/%s' % (adminurl, k), v)) + categorylinks.AddItem(Link(url, Bold('[%s]' % label))) + else: + categorylinks.AddItem(Link(url, label)) # Add all the links to the links table... linktable.AddRow([categorylinks, otherlinks]) linktable.AddRowInfo(max(linktable.GetCurrentRowIndex(), 0), @@ -467,47 +457,46 @@ def show_variables(mlist, category, cgidata, doc, form): # Special case for members section. return membership_options(mlist, cgidata, doc, form) - options = get_config_options(mlist, category) + options = mlist.GetConfigInfo()[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) - break + categories = mlist.GetConfigCategories() + label = _(categories[category][0]) table.AddRow([Center(Header(2, label))]) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2, bgcolor=mm_cfg.WEB_HEADER_COLOR) - # Convenience - def column_header(table=table): - table.AddRow([Center(Bold(_('Description'))), - Center(Bold(_('Value')))]) - table.AddCellInfo(table.GetCurrentRowIndex(), 0, width='15%') - table.AddCellInfo(table.GetCurrentRowIndex(), 1, width='85%') + # 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, types.StringType): + 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%') - did_col_header = 0 for item in options: if type(item) == types.StringType: # 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... - if did_col_header: - item = "<center><i>" + item + "</i></center>" - 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... - column_header() - did_col_header = 1 + table.AddRow([Center(Italic(item))]) + table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) else: - if not did_col_header: - # ... but do col header before anything else. - column_header() - did_col_header = 1 add_options_table_item(mlist, category, table, item) table.AddRow(['<br>']) table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) @@ -605,7 +594,6 @@ def get_item_gui_value(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 popup elif kind == mm_cfg.Select: if params: values, legend, selected = params @@ -614,7 +602,54 @@ def get_item_gui_value(mlist, kind, varname, params): legend = map(_, map(Utils.GetLanguageDescr, values)) selected = values.index(mlist.preferred_language) return SelectOptions(varname, values, legend, selected) - + elif kind == mm_cfg.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=0, 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=1) + return table def get_item_gui_description(mlist, category, varname, descr, detailsp): @@ -670,7 +705,7 @@ def membership_options(mlist, cgidata, doc, form): # 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 - all = mlist.GetMembers() + mlist.GetDigestMembers() + all = mlist.getMembers() all.sort(lambda x, y: cmp(x.lower(), y.lower())) # See if the query has a regular expression regexp = '' @@ -765,22 +800,28 @@ def membership_options(mlist, cgidata, doc, form): for i in range(9): usertable.AddCellInfo(rowindex, i, bgcolor=mm_cfg.WEB_ADMINITEM_COLOR) # Find the longest name in the list + longest = 0 if members: - longest = max([len(s) for s in members]) - else: - longest = 0 + names = filter(None, [mlist.getMemberName(s) for s in members]) + if names: + longest = max([len(s) for s in names]) # Now populate the rows for addr in members: link = Link(mlist.GetOptionsURL(addr, obscure=1), - mlist.GetUserSubscribedAddress(addr)) -# name = TextBox(addr + '_realname', 'NOT YET IMPLEMENTED', size=longest) + mlist.getMemberCPAddress(addr)) + fullname = mlist.getMemberName(addr) + if fullname: + name = TextBox(addr + '_realname', fullname, size=longest).Format() + else: + name = '' cells = [link.Format() + '<br>' + - #name.Format() + '\n' + + name + Hidden('user', urllib.quote(addr)).Format(), Center(CheckBox(addr + '_subscribed', 'on', 1).Format()), ] for opt in ('hide', 'nomail', 'ack', 'notmetoo'): - if mlist.GetUserOption(addr, MailCommandHandler.option_info[opt]): + if mlist.getMemberOption(addr, + MailCommandHandler.option_info[opt]): value = 'on' checked = 1 else: @@ -792,7 +833,8 @@ def membership_options(mlist, cgidata, doc, form): cells.append(Center(CheckBox(addr + '_digest', 'off', 0).Format())) else: cells.append(Center(CheckBox(addr + '_digest', 'on', 1).Format())) - if mlist.GetUserOption(addr, MailCommandHandler.option_info['plain']): + if mlist.getMemberOption(addr, + MailCommandHandler.option_info['plain']): value = 'on' checked = 1 else: @@ -1041,6 +1083,14 @@ def change_options(mlist, category, cgidata, doc): add_error_message( doc, _('Administator passwords did not match'), tag=_('Error: ')) + + # Give the individual gui item a chance to process the form data + categories = mlist.GetConfigCategories() + label, gui = categories[category] + if hasattr(gui, 'HandleForm'): + gui.HandleForm(mlist, cgidata, doc) + return + # for some reason, the login page mangles important values for the list # such as .real_name so we only process these changes if the category # is not "members" and the request is not from the login page @@ -1058,7 +1108,7 @@ def change_options(mlist, category, cgidata, doc): # page_setting = int(cgidata["subscribe_policy"].value) cgidata["subscribe_policy"].value = str(page_setting + 1) - opt_list = get_config_options(mlist, category) + opt_list = mlist.GetConfigInfo()[category] for item in opt_list: if type(item) <> types.TupleType or len(item) < 5: continue @@ -1172,9 +1222,10 @@ def change_options(mlist, category, cgidata, doc): unsubscribe_success = [] for addr in names: try: - mlist.DeleteMember(addr, whence='admin mass unsub', - admin_notif=send_unsub_notifications, - userack=userack) + mlist.ApprovedDeleteMember( + addr, whence='admin mass unsub', + admin_notif=send_unsub_notifications, + userack=userack) unsubscribe_success.append(addr) except Errors.MMNoSuchUserError: unsubscribe_errors.append(addr) @@ -1203,30 +1254,35 @@ def change_options(mlist, category, cgidata, doc): for user in users: if not cgidata.has_key('%s_subscribed' % (user)): try: - mlist.DeleteMember(user) + mlist.ApprovedDeleteMember(user) except Errors.MMNoSuchUserError: errors.append((user, _('Not subscribed'))) continue value = cgidata.has_key('%s_digest' % user) try: - mlist.SetUserDigest(user, value, force=1) - except (Errors.MMNotAMemberError, - Errors.MMAlreadyDigested, - Errors.MMAlreadyUndigested): + mlist.setMemberOption(user, mm_cfg.Digests, value) + except (Errors.NotAMemberError, + Errors.AlreadyReceivingDigests, + Errors.AlreadyReceivingRegularDeliveries, + Errors.CantDigestError, + Errors.MustDigestError): pass - if cgidata.has_key(user+'_language'): - newlang = cgidata[user+'_language'].value - oldlang = mlist.GetPreferredLanguage(user) - if newlang <> oldlang: - mlist.SetPreferredLanguage(user, newlang) + newname = cgidata.getvalue(user+'_realname') + if newname: + mlist.setMemberName(user, newname) + + newlang = cgidata.getvalue(user+'_language') + oldlang = mlist.GetPreferredLanguage(user) + if newlang and newlang <> oldlang: + mlist.setMemberLanguage(user, newlang) for opt in ("hide", "nomail", "ack", "notmetoo", "plain"): opt_code = MailCommandHandler.option_info[opt] - if cgidata.has_key("%s_%s" % (user, opt)): - mlist.SetUserOption(user, opt_code, 1, save_list=0) + if cgidata.has_key('%s_%s' % (user, opt)): + mlist.setMemberOption(user, opt_code, 1) else: - mlist.SetUserOption(user, opt_code, 0, save_list=0) + mlist.setMemberOption(user, opt_code, 0) if errors: doc.AddItem(Header(5, _("Error Unsubscribing:"))) items = ['%s -- %s' % (x[0], x[1]) for x in errors] @@ -1239,8 +1295,3 @@ def add_error_message(doc, errmsg, tag='Warning: ', *args): doc.AddItem(Header(3, Bold(FontAttr( _(tag), color=mm_cfg.WEB_ERROR_COLOR, size="+2")).Format() + Italic(errmsg % args).Format())) - - - -def get_config_options(mlist, category): - return mlist.GetConfigInfo()[category] |
