I send you a piece of file with marking already done, in order contributors can note how to do markings. I have tried (where possible) to take apart HTML code. Cheers PD: There may be errors, I have done it in a hurry. -- ___ / F \ [[[]]]] ( O O ) #----------------0000--(_)--0000---------------# | Juan Carlos Rey Anaya (jcrey@uma.es) | | Servicio Central de informática | | Universidad de Málaga - España | #----------------------------------------------# # Solo se que cada vez se menos :-| # #----------------------------------------------# 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 if cgidata.has_key('bounce_matching_headers'): pairs = mlist.parse_matching_header_opt() if len(cgidata.keys()): ChangeOptions(mlist, category, cgidata, doc) mlist.CheckValues() # 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.''')) 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.''')) 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") finally: mlist.Save() mlist.Unlock() # Form Production: def FormatAdminOverview(error=None): "Present a general welcome and itemize the (public) lists." doc = Document() legend = _("%s mailing lists - Admin Links") % mm_cfg.DEFAULT_HOST_NAME doc.SetTitle(legend) table = Table(border=0, width="100%") table.AddRow([Center(Header(2, legend))]) table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2, bgcolor="#99ccff") advertised = [] names = Utils.list_names() names.sort() for n in names: l = MailList.MailList(n, lock=0) if l.advertised: advertised.append(l) if error: greeting = FontAttr(error, color="ff5060", size="+1") else: greeting = _("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, ) else: welcome_items = ( greeting, _("<p>" " Below is the collection of publicly-advertised "), Link(mm_cfg.MAILMAN_URL, "mailman"), _(" mailing lists on %s.") % mm_cfg.DEFAULT_HOST_NAME, (_(' Click on a list name to visit the configuration pages' ' for that list.') ) ) 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 %slist name appended.<p>") % ((error and _("right ")) or "")) + _(" 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" % mm_cfg.MAILMAN_OWNER, mm_cfg.MAILMAN_OWNER), ".)<p>" ) ) table.AddRow([apply(Container, welcome_items)]) 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( [Link(l.GetScriptURL('admin'), Bold(l.real_name)), l.description or Italic(_('[no description available]')), ]) doc.AddItem(table) doc.AddItem('<hr>') doc.AddItem(MailmanLogo()) print doc.Format(bgcolor="#ffffff") def FormatConfiguration(doc, mlist, category, category_suffix, cgi_data): """Produce the overall doc, *except* any processing error messages.""" for k, v in CATEGORIES: if k == category: label = v doc.SetTitle('%s Administration (%s)' % (mlist.real_name, label)) doc.AddItem(Center(Header(2, _('%s mailing list administration<br>%s Section') % (mlist.real_name, label)))) doc.AddItem('<hr>') linktable = Table(valign="top") linktable.AddRow([Center(Bold(_("Configuration Categories"))), Center(Bold(_("Other Administrative Activities")))]) adminurl = mlist.GetScriptURL('admin') otherlinks = UnorderedList() otherlinks.AddItem(Link(mlist.GetScriptURL('admindb'), _('Tend to pending administrative requests'))) otherlinks.AddItem(Link(mlist.GetScriptURL('listinfo'), _('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('%s/logout' % adminurl, # TBD: What I really want is a blank line :/ '<FONT SIZE="+2"><b>Logout</b></FONT>')) categorylinks = UnorderedList() for k, v in CATEGORIES: if k == category: categorylinks.AddItem("<em>%s</em>" % v) else: categorylinks.AddItem(Link("%s/%s" % (adminurl, k), v)) linktable.AddRow([categorylinks, otherlinks]) linktable.AddRowInfo(max(linktable.GetCurrentRowIndex(), 0), valign="top") doc.AddItem(linktable) doc.AddItem('<hr>') if category_suffix: encoding = None if category_suffix == 'autoreply': # these have file uploads encoding = 'multipart/form-data' form = Form('%s/%s' % (adminurl, category_suffix), encoding=encoding) else: form = Form(adminurl) doc.AddItem(form) if category == 'general': 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.%s<p>") % andpassmsg) form.AddItem(FormatOptionsSection(category, mlist, cgi_data)) if category == 'general': form.AddItem(Center(FormatPasswordStuff())) form.AddItem("<p>") form.AddItem(Center(FormatSubmit())) form.AddItem(mlist.GetMailmanFooter()) def FormatOptionsSection(category, mlist, cgi_data): """Produce the category-specific options table.""" if category == 'members': # Special case for members section. return FormatMembershipOptions(mlist, cgi_data) options = GetConfigOptions(mlist, category) big_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, 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 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>" big_table.AddRow([item]) big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 0, colspan=2) if not did_col_header: # Do col header after very first string descr, if any... ColHeader() did_col_header = 1 else: if not did_col_header: # ... but do col header before anything else. ColHeader() 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 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) table.AddRow([descr, val]) table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 1, bgcolor="#cccccc") table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, bgcolor="#cccccc") def FormatOptionHelp(doc, varref, mlist): item = None reflist = string.split(varref, '/') 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%") legend = (_('%s Mailing list Configuration Help<br><em>%s</em> Option') % (mlist.real_name, varname)) header.AddRow([Center(Header(3, legend))]) header.AddCellInfo(max(header.GetCurrentRowIndex(), 0), 0, colspan=2, bgcolor="#99ccff") doc.SetTitle(_("Mailman %s List Option Help") % varname) 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 %s options page.') % category )) 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: elaboration = None varname, kind, params, dependancies, descr = table_entry elif len(table_entry) == 6: varname, kind, params, dependancies, descr, elaboration = table_entry else: raise ValueError, (_("Badly formed options entry:\n %s") % table_entry) return (varname, kind, params, dependancies, descr, elaboration) def GetItemGuiValue(mlist, kind, varname, params): """Return a representation of an item's settings.""" if kind == mm_cfg.Radio or kind == mm_cfg.Toggle: # # if we are sending 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[0] == '_': checked = 0 else: checked = getattr(mlist, varname) if varname == 'subscribe_policy' and not mm_cfg.ALLOW_OPEN_SUBSCRIBE: checked = checked - 1 return RadioButtonArray(varname, params, checked) elif (kind == mm_cfg.String or kind == mm_cfg.Email or kind == mm_cfg.Host or kind == mm_cfg.Number): return TextBox(varname, getattr(mlist, varname), params) elif kind == mm_cfg.Text: if params: r, c = params else: r, c = None, None val = getattr(mlist, varname) if not val: val = '' return TextArea(varname, val, r, c) elif kind == mm_cfg.EmailList: if params: r, c = params else: r, c = None, None res = string.join(getattr(mlist, varname), '\n') return TextArea(varname, res, r, c, wrap='off') elif kind == mm_cfg.FileUpload: # like a text area, but also with uploading if params: r, c = params else: r, c = None, None val = getattr(mlist, varname) if not val: val = '' container = Container() container.AddItem(_('<em>Enter the text below, or...</em><br>')) container.AddItem(TextArea(varname, val, r, c)) container.AddItem(_('<br><em>...specify a file to upload</em><br>')) container.AddItem(FileUpload(varname+'_upload', r, c)) return container 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! """ if detailsp: text = Container('<div ALIGN="right">' + descr + ' ', Link(mlist.GetScriptURL('admin') + '/?VARHELP=' + category + '/' + varname, '(Details)'), '</div>').Format() else: text = '<div ALIGN="right">' + descr + '</div>' if varname[0] == '_': text = text + _('''<div ALIGN="right"><br><em><strong>Note:</strong> setting this value performs an immediate action but does not modify permanent state.</em></div>''') return text def FormatMembershipOptions(mlist, cgi_data): container = Container() header = Table(width="100%") header.AddRow([Center(Header(2, _("Membership Management")))]) header.AddCellInfo(max(header.GetCurrentRowIndex(), 0), 0, colspan=2, bgcolor="#99ccff") container.AddItem(header) user_table = Table(width="90%", border='2') user_table.AddRow([Center(Header(4, _("Membership List")))]) user_table.AddCellInfo(user_table.GetCurrentRowIndex(), user_table.GetCurrentCellIndex(), bgcolor="#cccccc", colspan=8) user_table.AddRow( [Center(Italic(_("(%s members total, max. %s at a time displayed)") % (len(mlist.members) + len(mlist.digest_members), mlist.admin_member_chunksize)))]) user_table.AddCellInfo(user_table.GetCurrentRowIndex(), user_table.GetCurrentCellIndex(), bgcolor="#cccccc", colspan=8) user_table.AddRow(map(Center, [_('member address'), _('subscr'), _('hide'), _('nomail'), _('ack'), _('not metoo'), _('digest'), _('plain')])) rowindex = user_table.GetCurrentRowIndex() for i in range(8): user_table.AddCellInfo(rowindex, i, bgcolor='#cccccc') 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"): chunk = 0 else: chunk = string.atoi(cgi_data["chunk"].value) all = chunks[chunk] 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 = apply(UnorderedList, tuple(buttons)) footer = footer + buttons.Format() + "<p>" else: all.sort() footer = "<p>" for member in all: mtext = '<a href="%s">%s</a>' % ( mlist.GetOptionsURL(member, obscure=1), mlist.GetUserSubscribedAddress(member)) cells = [mtext + "<input type=hidden name=user value=%s>" % (member), Center(CheckBox(member + "_subscribed", "on", 1).Format())] for opt in ("hide", "nomail", "ack", "notmetoo"): if mlist.GetUserOption(member,MailCommandHandler.option_info[opt]): value = "on" checked = 1 else: value = "off" checked = 0 box = CheckBox("%s_%s" % (member, opt), value, checked) cells.append(Center(box.Format())) if mlist.members.has_key(member): cells.append(Center(CheckBox(member + "_digest", "off", 0).Format())) else: cells.append(Center(CheckBox(member + "_digest", "on", 1).Format())) if mlist.GetUserOption(member,MailCommandHandler.option_info['plain']): value = 'on' checked = 1 else: value = 'off' checked = 0 cells.append(Center(CheckBox('%s_plain' % member, value, checked))) user_table.AddRow(cells) 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>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)')) container.AddItem(legend.Format()) container.AddItem(footer) t = Table(width="90%") t.AddRow([Center(Header(4, _("Mass Subscribe Members")))]) t.AddCellInfo(t.GetCurrentRowIndex(), t.GetCurrentCellIndex(), bgcolor="#cccccc", colspan=8) if mlist.send_welcome_msg: nochecked = 0 yeschecked = 1 else: nochecked = 1 yeschecked = 0 t.AddRow([("<b>1.</b>" + _("Send Welcome message to this batch? ") + RadioButton("send_welcome_msg_to_this_batch", 0, nochecked).Format() + _(" no ") + RadioButton("send_welcome_msg_to_this_batch", 1, yeschecked).Format() + _(" yes "))]) t.AddRow(["<b>2.</b>" + _("Enter one address per line:") + "<p>"]) container.AddItem(Center(t)) container.AddItem(Center(TextArea(name='subscribees', rows=10,cols=60,wrap=None))) return container def FormatPasswordStuff(): change_pw_table = Table(bgcolor="#99cccc", border=0, cellspacing=0, cellpadding=2, valign="top") change_pw_table.AddRow( [Bold(Center(_('To Change The Administrator Password')))]) 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>', PasswordBox('adminpw')]) new = Table(bgcolor="#99cccc", border=1, cellspacing=0, cellpadding=2, valign="top") new.AddRow(['<div ALIGN="right">' + _(" Enter new password:") + '</div>', PasswordBox('newpw')]) new.AddRow(['<div ALIGN="right">' + _("Confirm new password:") + '</div>', PasswordBox('confirmpw')]) change_pw_table.AddRow([old, new]) change_pw_table.AddCellInfo(1, 0, align="left", valign="top") #change_pw_table.AddCellInfo(1, 1, align="left", valign="top") return change_pw_table def FormatSubmit(): submit = Table(bgcolor="#99ccff", border=0, cellspacing=0, cellpadding=2) submit.AddRow([Bold(SubmitButton('submit', _('Submit Your Changes')))]) submit.AddCellInfo(submit.GetCurrentRowIndex(), 0, align="middle") return submit # XXX klm - looks like turn_on_moderation is orphaned. #turn_on_moderation = 0 # Options processing def GetValidValue(mlist, prop, my_type, val, dependant): if my_type == mm_cfg.Radio or my_type == mm_cfg.Toggle: if type(val) <> types.IntType: try: val = int(val) except ValueError: pass # Don't know what to do here... return val elif my_type == mm_cfg.String or my_type == mm_cfg.Text: return val elif my_type == mm_cfg.Email: try: Utils.ValidateEmail(val) return val except Errors.EmailAddressError: # TBD: should have a way of displaying the results of the # operation. pass # Revert to the old value. return getattr(mlist, prop) elif my_type == mm_cfg.EmailList: def SafeValidAddr(addr): try: Utils.ValidateEmail(addr) return 1 except Errors.EmailAddressError: return 0 val = filter(SafeValidAddr, map(string.strip, string.split(val, '\n'))) ## if dependant and len(val): ## # Wait till we've set everything to turn it on, ## # as we don't want to clobber our special case. ## # XXX klm - looks like turn_on_moderation is orphaned? ## turn_on_moderation = 1 return val elif my_type == mm_cfg.Host: return val ## ## This code is sendmail dependant, so we'll just live w/o ## the error checking for now. ## ## # Shouldn't have to read in the whole file. ## file = open('/etc/sendmail.cf', 'r') ## lines = string.split(file.read(), '\n') ## file.close() ## def ConfirmCWEntry(item): ## return item[0:2] == 'Cw' ## lines = filter(ConfirmCWEntry, lines) ## if not len(lines): ## # Revert to the old value. ## return getattr(list, prop) ## for line in lines: ## if string.lower(string.strip(line[2:])) == string.lower(val): ## return val ## return getattr(list, prop) elif my_type == mm_cfg.Number: num = -1 try: num = int(val) except ValueError: # TBD: a float??? try: num = float(val) except ValueError: pass if num < 0: return getattr(mlist, prop) return num else: # Should never get here... return val def ChangeOptions(mlist, category, cgi_info, document): confirmed = 0 if cgi_info.has_key('newpw'): if cgi_info.has_key('confirmpw'): if cgi_info.has_key('adminpw') and cgi_info['adminpw'].value: try: mlist.ConfirmAdminPassword(cgi_info['adminpw'].value) confirmed = 1 except Errors.MMBadPasswordError: AddErrorMessage(document, _('Incorrect administrator password'), tag='Error: ') if confirmed: new = string.strip(cgi_info['newpw'].value) confirm = string.strip(cgi_info['confirmpw'].value) if new == '' and confirm == '': AddErrorMessage(document, _('Empty admin passwords are not allowed'), tag='Error: ') elif new == confirm: mlist.password = crypt(new, Utils.GetRandomSeed()) # Re-authenticate (to set new cookie) mlist.WebAuthenticate(password=new, cookie='admin') else: AddErrorMessage(document, _('Passwords did not match'), tag='Error: ') else: AddErrorMessage(document, _('You must type in your new password twice'), tag='Error: ') # # 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 # -scott 19980515 # if category != 'members' and \ not cgi_info.has_key("request_login") and \ len(cgi_info.keys()) > 1: # then if cgi_info.has_key("subscribe_policy"): if not mm_cfg.ALLOW_OPEN_SUBSCRIBE: # # we have to add one to the value because the # page didn't present an open list as an option # page_setting = string.atoi(cgi_info["subscribe_policy"].value) cgi_info["subscribe_policy"].value = str(page_setting + 1) opt_list = GetConfigOptions(mlist, category) for item in opt_list: if type(item) <> types.TupleType or len(item) < 5: continue property, kind, args, deps, desc = item[0:5] if cgi_info.has_key(property+'_upload') and \ cgi_info[property+'_upload'].value: val = cgi_info[property+'_upload'].value elif not cgi_info.has_key(property): continue else: val = cgi_info[property].value value = GetValidValue(mlist, property, kind, val, deps) # # This is an ugly, ugly hack if property[0] == '_': # TBD: When turning on usenet->mail gating we want to # automatically catch up the newsgroup otherwise the mailing # list will suddently get flooded. There should be a much # better way to do this (or for the admin to specify they want # this). if property == '_mass_catchup' and value: mlist.usenet_watermark = None elif getattr(mlist, property) <> value: # TBD: Ensure that mlist.real_name differs only in letter # case. Otherwise a security hole can potentially be opened # when using an external archiver. This seems ad-hoc and # could use a more general security policy. if property == 'real_name' and \ string.lower(value) <> string.lower(mlist._internal_name): # then don't install this value. document.AddItem(_("""<p><b>real_name</b> attribute not changed! It must differ from the list's name by case only.<p>""")) continue setattr(mlist, property, value) # # mass subscription processing for members category # if cgi_info.has_key('subscribees'): name_text = cgi_info['subscribees'].value name_text = string.replace(name_text, '\r', '') names = filter(None, map(string.strip, string.split(name_text, '\n'))) send_welcome_msg = string.atoi( cgi_info["send_welcome_msg_to_this_batch"].value) digest = 0 if not mlist.digestable: digest = 0 if not mlist.nondigestable: digest = 1 subscribe_errors = [] subscribe_success = [] result = mlist.ApprovedAddMembers(names, None, digest, send_welcome_msg) for name in result.keys(): if result[name] is None: subscribe_success.append(name) else: # `name' was not subscribed, find out why. On failures, # result[name] is set from sys.exc_info()[:2] e, v = result[name] if e is Errors.MMAlreadyAMember: subscribe_errors.append((name, _('Already a member'))) elif e is Errors.MMBadEmailError: if name == '': name = '<blank line>' subscribe_errors.append( (name, _("Bad/Invalid email address"))) elif e is Errors.MMHostileAddress: subscribe_errors.append( (name, _("Hostile Address (illegal characters)"))) if subscribe_success: document.AddItem(Header(5, _("Successfully Subscribed:"))) document.AddItem(apply(UnorderedList, tuple((subscribe_success)))) document.AddItem("<p>") # ApprovedAddMembers will already have saved the list for us. if subscribe_errors: document.AddItem(Header(5, _("Error Subscribing:"))) items = map(lambda x: "%s -- %s" % (x[0], x[1]), subscribe_errors) document.AddItem(apply(UnorderedList, tuple((items)))) document.AddItem("<p>") # # do the user options for members category # if cgi_info.has_key('user'): user = cgi_info["user"] if type(user) is type([]): users = [] for ui in range(len(user)): users.append(user[ui].value) else: users = [user.value] errors = [] for user in users: if not cgi_info.has_key('%s_subscribed' % (user)): try: mlist.DeleteMember(user) except Errors.MMNoSuchUserError: errors.append((user, _('Not subscribed'))) continue value = cgi_info.has_key('%s_digest' % user) try: mlist.SetUserDigest(user, value, force=1) except (Errors.MMNotAMemberError, Errors.MMAlreadyDigested, Errors.MMAlreadyUndigested): pass for opt in ("hide", "nomail", "ack", "notmetoo", "plain"): opt_code = MailCommandHandler.option_info[opt] if cgi_info.has_key("%s_%s" % (user, opt)): mlist.SetUserOption(user, opt_code, 1, save_list=0) else: 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) document.AddItem(apply(UnorderedList, tuple((items)))) document.AddItem("<p>") def AddErrorMessage(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): return mlist.GetConfigInfo()[category]