1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
|
# Copyright (C) 1998,1999,2000 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 os
import string
import types
import cgi
from errno import ENOENT
from Mailman import mm_cfg
from Mailman import Utils
from Mailman import MailList
from Mailman import Errors
from Mailman import Message
from Mailman.Cgi import Auth
from Mailman.htmlformat import *
from Mailman.Logging.Syslog import syslog
def handle_no_list(doc, extra=''):
doc.SetTitle('Mailman Admindb Error')
doc.AddItem(Header(2, 'Mailman Admindb Error'))
doc.AddItem(extra)
doc.AddItem('You must specify a list name. Here is the ')
link = mm_cfg.DEFAULT_URL
if link[-1] <> '/':
link = link + '/'
link = link + 'admin'
doc.AddItem(Link(link, 'list of available mailing lists.'))
print doc.Format(bgcolor="#ffffff")
def main():
doc = Document()
# figure out which list we're going to process
try:
path = os.environ['PATH_INFO']
except KeyError:
handle_no_list(doc)
return
# get URL components. the list name should be the zeroth part
parts = Utils.GetPathPieces(path)
try:
listname = string.lower(parts[0])
except IndexError:
handle_no_list(doc)
return
# now that we have the list name, create the list object
try:
mlist = MailList.MailList(listname)
except Errors.MMListError, e:
handle_no_list(doc, 'No such list <em>%s</em><p>' % listname)
syslog('error', 'No such list "%s": %s\n' % (listname, e))
return
#
# now we must authorize the user to view this page, and if they are, to
# handle both the printing of the current outstanding requests, and the
# selected actions
try:
cgidata = cgi.FieldStorage()
try:
Auth.authenticate(mlist, cgidata)
except Auth.NotLoggedInError, e:
Auth.loginpage(mlist, 'admindb', e.message)
return
# If this is a form submission, then we'll process the requests and
# print the results. otherwise (there are no keys in the form), we'll
# print out the list of pending requests
#
if len(cgidata.keys()):
doc.SetTitle("%s Admindb Results" % mlist.real_name)
HandleRequests(mlist, doc, cgidata)
else:
doc.SetTitle("%s Admindb" % mlist.real_name)
PrintRequests(mlist, doc)
text = doc.Format(bgcolor="#ffffff")
print text
finally:
mlist.Save()
mlist.Unlock()
def PrintRequests(mlist, doc):
# The only types of requests we know about are add member and post.
# Anything else that might have gotten in here somehow we'll just ignore
# (This should never happen unless someone is hacking at the code).
doc.AddItem(Header(2, 'Administrative requests for mailing list: <em>' +
mlist.real_name + '</em>'))
# short circuit for when there are no pending requests
if not mlist.NumRequestsPending():
doc.AddItem('There are no pending requests. You can now ')
doc.AddItem(
Link(mlist.GetRelativeScriptURL('admin'),
Italic('view or edit the list configuration information.')))
doc.AddItem(mlist.GetMailmanFooter())
return
doc.AddItem(Utils.maketext(
'admindbpreamble.html', {'listname': mlist.real_name}, raw=1))
doc.AddItem(
Link(mlist.GetRelativeScriptURL('admin'),
Italic('view or edit the list configuration information')))
doc.AddItem('.<p>')
form = Form(mlist.GetRelativeScriptURL('admindb'))
doc.AddItem(form)
form.AddItem(SubmitButton('submit', 'Submit All Data'))
#
# Add the subscription request section
subpendings = mlist.GetSubscriptionIds()
if subpendings:
form.AddItem('<hr>')
form.AddItem(Center(Header(2, 'Subscription Requests')))
t = Table(border=2)
t.AddRow([
Bold('Address'),
Bold('Your Decision'),
Bold('If you refuse this subscription, please explain (optional)')
])
for id in subpendings:
PrintAddMemberRequest(mlist, id, t)
form.AddItem(t)
# Post holds are now handled differently
heldmsgs = mlist.GetHeldMessageIds()
total = len(heldmsgs)
if total:
count = 1
for id in heldmsgs:
info = mlist.GetRecord(id)
PrintPostRequest(mlist, id, info, total, count, form)
count = count + 1
form.AddItem('<hr>')
form.AddItem(SubmitButton('submit', 'Submit All Data'))
doc.AddItem(mlist.GetMailmanFooter())
def PrintAddMemberRequest(mlist, id, table):
time, addr, passwd, digest = mlist.GetRecord(id)
table.AddRow([addr,
RadioButtonArray(id, ('Subscribe', 'Refuse'),
values=(mm_cfg.SUBSCRIBE, mm_cfg.REJECT)),
TextBox('comment-%d' % id, size=60)
])
def PrintPostRequest(mlist, id, info, total, count, form):
# For backwards compatibility with pre 2.0beta3
if len(info) == 5:
ptime, sender, subject, reason, filename = info
msgdata = {}
else:
ptime, sender, subject, reason, filename, msgdata = info
form.AddItem('<hr>')
msg = 'Posting Held for Approval'
if total <> 1:
msg = msg + ' (%d of %d)' % (count, total)
form.AddItem(Center(Header(2, msg)))
try:
fp = open(os.path.join(mm_cfg.DATA_DIR, filename))
msg = Message.Message(fp)
fp.close()
text = msg.body[:mm_cfg.ADMINDB_PAGE_TEXT_LIMIT]
except IOError, (code, msg):
if code == ENOENT:
form.AddItem('<em>Message with id #%d was lost.' % id)
form.AddItem('<p>')
# TBD: kludge to remove id from requests.db. value==2 means
# discard the message.
try:
mlist.HandleRequest(id, mm_cfg.DISCARD)
except Errors.LostHeldMessage:
pass
return
raise
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:'), subject])
t.AddCellInfo(row+1, col-1, align='right')
t.AddRow([Bold('Reason:'), reason])
t.AddCellInfo(row+2, col-1, align='right')
t.AddRow([
Bold('Action:'),
RadioButtonArray(id, ('Defer', 'Approve', 'Reject', 'Discard'),
values=(mm_cfg.DEFER, mm_cfg.APPROVE, mm_cfg.REJECT,
mm_cfg.DISCARD),
checked=0)
])
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.GetAdminEmail()).Format()
])
t.AddRow([
Bold('If you reject this post,<br>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, string.join(msg.headers, ''),
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, text, rows=10, cols=80)])
t.AddCellInfo(row+1, col-1, align='right')
form.AddItem(t)
form.AddItem('<p>')
def HandleRequests(mlist, doc, cgidata):
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, so just ignore this id
continue
except Errors.MMAlreadyAMember, v:
erroraddrs.append(v)
# save the list and print the results
mlist.Save()
doc.AddItem(Header(2, 'Database Updated...'))
if erroraddrs:
for addr in erroraddrs:
doc.AddItem(`addr` + ' is already a member<br>')
|