# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """Produce and process the pending-approval items for a list.""" import sys import os import types import cgi import errno import signal import email from Mailman import mm_cfg from Mailman import Utils from Mailman import MailList from Mailman import Errors from Mailman import Message from Mailman import i18n from Mailman.ListAdmin import readMessage from Mailman.Cgi import Auth from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog EMPTYSTRING = '' NL = '\n' # Set up i18n. Until we know which list is being requested, we use the # server's default. _ = i18n._ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) def main(): # Figure out which list is being requested parts = Utils.GetPathPieces() if not parts: handle_no_list() return listname = parts[0].lower() try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError, e: handle_no_list(_('No such list %(listname)s')) syslog('error', 'No such list "%s": %s\n', listname, e) return # Now that we know which list to use, set the system's language to it. i18n.set_language(mlist.preferred_language) # Make sure the user is authorized to see this page. cgidata = cgi.FieldStorage(keep_blank_values=1) if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthListModerator, mm_cfg.AuthSiteAdmin), cgidata.getvalue('adminpw', '')): if cgidata.has_key('adminpw'): # This is a re-authorization attempt msg = Bold(FontSize('+1', _('Authorization failed.'))).Format() else: msg = '' Auth.loginpage(mlist, 'admindb', msg=msg) return # Set up the results document doc = Document() doc.set_language(mlist.preferred_language) # We need a signal handler to catch the SIGTERM that can come from Apache # when the user hits the browser's STOP button. See the comment in # admin.py for details. # # BAW: Strictly speaking, the list should not need to be locked just to # read the request database. However the request database asserts that # the list is locked in order to load it and it's not worth complicating # that logic. def sigterm_handler(signum, frame, mlist=mlist): # Make sure the list gets unlocked... mlist.Unlock() # ...and ensure we exit, otherwise race conditions could cause us to # enter MailList.Save() while we're in the unlocked state, and that # could be bad! sys.exit(0) mlist.Lock() try: # Install the emergency shutdown signal handler signal.signal(signal.SIGTERM, sigterm_handler) realname = mlist.real_name if not cgidata.keys(): # If this is not a form submission (i.e. there are no keys in the # form), then all we don't need to do much special. doc.SetTitle(_('%(realname)s Administrative Database')) else: # This is a form submission doc.SetTitle(_('%(realname)s Administrative Database Results')) process_form(mlist, doc, cgidata) # Now print the results and we're done show_requests(mlist, doc) mlist.Save() finally: mlist.Unlock() print doc.Format() def handle_no_list(msg=''): # Print something useful if no list was given. doc = Document() doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) header = _('Mailman Administrative Database Error') doc.SetTitle(header) doc.AddItem(Header(2, header)) doc.AddItem(msg) url = Utils.ScriptURL('admin', absolute=1) link = Link(url, _('list of available mailing lists.')).Format() doc.AddItem(_('You must specify a list name. Here is the %(link)s')) doc.AddItem('
')
# BAW: kludge to remove id from requests.db.
try:
mlist.HandleRequest(id, mm_cfg.DISCARD)
except Errors.LostHeldMessage:
pass
return
except email.Errors.MessageParseError:
form.AddItem(_('Message with id #%(id)d is corrupted.'))
# BAW: Should we really delete this, or shuttle it off for site admin
# to look more closely at?
form.AddItem(' ')
# BAW: kludge to remove id from requests.db.
try:
mlist.HandleRequest(id, mm_cfg.DISCARD)
except Errors.LostHeldMessage:
pass
return
# Get the header text and the message body excerpt
lines = []
chars = 0
for line in email.Iterators.body_line_iterator(msg):
lines.append(line)
chars += len(line)
if chars > mm_cfg.ADMINDB_PAGE_TEXT_LIMIT:
break
body = EMPTYSTRING.join(lines)[:mm_cfg.ADMINDB_PAGE_TEXT_LIMIT]
hdrtxt = NL.join(['%s: %s' % (k, v) for k, v in msg.items()])
# Okay, we've reconstituted the message just fine. Now for the fun part!
t = Table(cellspacing=0, cellpadding=0, width='100%')
t.AddRow([Bold(_('From:')), sender])
row, col = t.GetCurrentRowIndex(), t.GetCurrentCellIndex()
t.AddCellInfo(row, col-1, align='right')
t.AddRow([Bold(_('Subject:')), cgi.escape(subject)])
t.AddCellInfo(row+1, col-1, align='right')
t.AddRow([Bold(_('Reason:')), _(reason)])
t.AddCellInfo(row+2, col-1, align='right')
# We can't use a RadioButtonArray here because horizontal placement can be
# confusing to the user and vertical placement takes up too much
# real-estate. This is a hack!
buttons = Table(cellspacing="5", cellpadding="0")
buttons.AddRow(map(lambda x, s=' '*5: s+x+s,
(_('Defer'), _('Approve'), _('Reject'), _('Discard'))))
buttons.AddRow([Center(RadioButton(id, mm_cfg.DEFER, 1)),
Center(RadioButton(id, mm_cfg.APPROVE, 0)),
Center(RadioButton(id, mm_cfg.REJECT, 0)),
Center(RadioButton(id, mm_cfg.DISCARD, 0)),
])
t.AddRow([Bold(_('Action:')), buttons])
t.AddCellInfo(row+3, col-1, align='right')
t.AddRow([' ',
CheckBox('preserve-%d' % id, 'on', 0).Format() +
' ' + _('Preserve message for site administrator')
])
t.AddRow([' ',
CheckBox('forward-%d' % id, 'on', 0).Format() +
' ' + _('Additionally, forward this message to: ') +
TextBox('forward-addr-%d' % id, size=47,
value=mlist.GetOwnerEmail()).Format()
])
t.AddRow([
Bold(_('If you reject this post, ')
def process_form(mlist, doc, cgidata):
# Process the form and make updates to the admin database.
erroraddrs = []
for k in cgidata.keys():
formv = cgidata[k]
if type(formv) == types.ListType:
continue
try:
v = int(formv.value)
request_id = int(k)
except ValueError:
continue
# Get the action comment and reasons if present.
commentkey = 'comment-%d' % request_id
preservekey = 'preserve-%d' % request_id
forwardkey = 'forward-%d' % request_id
forwardaddrkey = 'forward-addr-%d' % request_id
# Defaults
comment = _('[No reason given]')
preserve = 0
forward = 0
forwardaddr = ''
if cgidata.has_key(commentkey):
comment = cgidata[commentkey].value
if cgidata.has_key(preservekey):
preserve = cgidata[preservekey].value
if cgidata.has_key(forwardkey):
forward = cgidata[forwardkey].value
if cgidata.has_key(forwardaddrkey):
forwardaddr = cgidata[forwardaddrkey].value
# Handle the request id
try:
mlist.HandleRequest(request_id, v, comment,
preserve, forward, forwardaddr)
except (KeyError, Errors.LostHeldMessage):
# That's okay, it just means someone else has already updated the
# database while we were staring at the page, so just ignore it
continue
except Errors.MMAlreadyAMember, v:
erroraddrs.append(v)
# save the list and print the results
doc.AddItem(Header(2, _('Database Updated...')))
if erroraddrs:
for addr in erroraddrs:
doc.AddItem(`addr` + _(' is already a member') + '
please explain (optional):')),
TextArea('comment-%d' % id, rows=4, cols=80,
text = Utils.wrap(msgdata.get('rejection-notice',
_('[No explanation given]')),
column=80))
])
row, col = t.GetCurrentRowIndex(), t.GetCurrentCellIndex()
t.AddCellInfo(row, col-1, align='right')
t.AddRow([Bold(_('Message Headers:')),
TextArea('headers-%d' % id, hdrtxt,
rows=10, cols=80)])
row, col = t.GetCurrentRowIndex(), t.GetCurrentCellIndex()
t.AddCellInfo(row, col-1, align='right')
t.AddRow([Bold(_('Message Excerpt:')),
TextArea('fulltext-%d' % id, body, rows=10, cols=80)])
t.AddCellInfo(row+1, col-1, align='right')
form.AddItem(t)
form.AddItem('
')