From eefd06f1b88b8ecbb23a9013cd223b72ca85c20d Mon Sep 17 00:00:00 2001
From: Barry Warsaw
Date: Sun, 25 Jan 2009 13:01:41 -0500
Subject: Push the source directory into a 'src' subdirectory so that
zc.buildout works correctly regardless of how it's used.
---
src/mailman/web/Cgi/admin.py | 1433 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1433 insertions(+)
create mode 100644 src/mailman/web/Cgi/admin.py
(limited to 'src/mailman/web/Cgi/admin.py')
diff --git a/src/mailman/web/Cgi/admin.py b/src/mailman/web/Cgi/admin.py
new file mode 100644
index 000000000..e5c6ee14b
--- /dev/null
+++ b/src/mailman/web/Cgi/admin.py
@@ -0,0 +1,1433 @@
+# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman 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 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman 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
+# GNU Mailman. If not, see
There currently are no publicly-advertised %(mailmanlink)s + mailing lists on %(hostname)s.'''), + ]) + else: + welcome.extend([ + greeting, + _('''
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 create a new mailing list. + +
General list information can be found at '''), + Link(Utils.ScriptURL('listinfo'), + _('the mailing list overview page')), + '.', + _('
(Send questions and comments to '), + Link('mailto:%s' % mailman_owner, mailman_owner), + '.)
', + ]) + + 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('
" % (varname, category, description)) + if elaboration: + doc.AddItem("%s
" % 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('
')
+ form.AddItem(Center(submit_button()))
+ doc.AddItem(Center(form))
+
+ doc.AddItem(_("""Warning: 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('')
+ 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
%(label)s Section'))))
+ doc.AddItem('
') + + # 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('
')
+ 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
member name'),
+ _('mod'), _('hide'),
+ _('nomail
[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() + '
' +
+ 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(
+ _('unsub -- Click on this to unsubscribe the member.'))
+ legend.AddItem(
+ _("""mod -- The user's personal moderation flag. If this is
+ set, postings from them will be moderated, otherwise they will be
+ approved."""))
+ legend.AddItem(
+ _("""hide -- Is the member's address concealed on
+ the list of subscribers?"""))
+ legend.AddItem(_(
+ """nomail -- Is delivery to the member disabled? If so, an
+ abbreviation will be given describing the reason for the disabled
+ delivery:
+
') + 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 = _('''
To view more members, click on the appropriate + range listed below:''') + 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() + '
') + 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 list administrators 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. + +
The list moderators 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 +list administrators can also tend to pending requests. + +
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 +general options section.""")]) + 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('
') + 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('
') + # 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('
') + 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('
') + # 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('
') + 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("
") -- cgit v1.2.3-70-g09d2