aboutsummaryrefslogtreecommitdiffstats
path: root/Mailman/Cgi/admin.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/Cgi/admin.py')
-rw-r--r--Mailman/Cgi/admin.py150
1 files changed, 134 insertions, 16 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py
index f3284e17..370a2507 100644
--- a/Mailman/Cgi/admin.py
+++ b/Mailman/Cgi/admin.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 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
@@ -32,6 +32,7 @@ from email.Utils import unquote, parseaddr, formataddr
from Mailman import mm_cfg
from Mailman import Utils
+from Mailman import Message
from Mailman import MailList
from Mailman import Errors
from Mailman import MemberAdaptor
@@ -77,8 +78,8 @@ def main():
# Send this with a 404 status.
print 'Status: 404 Not Found'
admin_overview(_('No such list <em>%(safelistname)s</em>'))
- syslog('error', 'admin.py access for non-existent list: %s',
- listname)
+ syslog('error', 'admin: No such list "%s": %s\n',
+ listname, e)
return
# Now that we know what list has been requested, all subsequent admin
# pages are shown in that list's preferred language.
@@ -88,7 +89,8 @@ def main():
# CSRF check
safe_params = ['VARHELP', 'adminpw', 'admlogin',
- 'letter', 'chunk', 'findmember']
+ 'letter', 'chunk', 'findmember',
+ 'legend']
params = cgidata.keys()
if set(params) - set(safe_params):
csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token'))
@@ -522,7 +524,7 @@ def show_results(mlist, doc, category, subcat, cgidata):
if category == 'members':
# Figure out which subcategory we should display
subcat = Utils.GetPathPieces()[-1]
- if subcat not in ('list', 'add', 'remove'):
+ if subcat not in ('list', 'add', 'remove', 'change'):
subcat = 'list'
# Add member category specific tables
form.AddItem(membership_options(mlist, subcat, cgidata, doc, form))
@@ -876,6 +878,13 @@ def membership_options(mlist, subcat, cgidata, doc, form):
container.AddItem(header)
mass_remove(mlist, container)
return container
+ if subcat == 'change':
+ header.AddRow([Center(Header(2, _('Address Change')))])
+ header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
+ bgcolor=mm_cfg.WEB_HEADER_COLOR)
+ container.AddItem(header)
+ address_change(mlist, container)
+ return container
# Otherwise...
header.AddRow([Center(Header(2, _('Membership List')))])
header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
@@ -902,6 +911,15 @@ def membership_options(mlist, subcat, cgidata, doc, form):
all.sort(lambda x, y: cmp(x.lower(), y.lower()))
# See if the query has a regular expression
regexp = cgidata.getvalue('findmember', '').strip()
+ try:
+ regexp = regexp.decode(Utils.GetCharSet(mlist.preferred_language))
+ except UnicodeDecodeError:
+ # This is probably a non-ascii character and an English language
+ # (ascii) list. Even if we didn't throw the UnicodeDecodeError,
+ # the input may have contained mnemonic or numeric HTML entites mixed
+ # with other characters. Trying to grok the real meaning out of that
+ # is complex and error prone, so we don't try.
+ pass
if regexp:
try:
cre = re.compile(regexp, re.IGNORECASE)
@@ -1151,7 +1169,8 @@ def membership_options(mlist, subcat, cgidata, doc, form):
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'))
+ link = Link(url + 'chunk=%d' % i + findfrag,
+ _('from %(start)s to %(end)s'))
buttons.append(link)
buttons = UnorderedList(*buttons)
container.AddItem(footer + buttons.Format() + '<p>')
@@ -1167,7 +1186,8 @@ def mass_subscribe(mlist, container):
Label(_('Subscribe these users now or invite them?')),
RadioButtonArray('subscribe_or_invite',
(_('Subscribe'), _('Invite')),
- 0, values=(0, 1))
+ mm_cfg.DEFAULT_SUBSCRIBE_OR_INVITE,
+ values=(0, 1))
])
table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
@@ -1241,6 +1261,38 @@ def mass_remove(mlist, container):
+def address_change(mlist, container):
+ # ADDRESS CHANGE
+ GREY = mm_cfg.WEB_ADMINITEM_COLOR
+ table = Table(width='90%')
+ table.AddRow([Italic(_("""To change a list member's address, enter the
+ member's current and new addresses below. Use the check boxes to send
+ notice of the change to the old and/or new address(es)."""))])
+ table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=3)
+ table.AddRow([
+ Label(_("Member's current address")),
+ TextBox(name='change_from'),
+ CheckBox('notice_old', 'yes', 0).Format() +
+ '&nbsp;' +
+ _('Send notice')
+ ])
+ table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
+ table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
+ table.AddCellInfo(table.GetCurrentRowIndex(), 2, bgcolor=GREY)
+ table.AddRow([
+ Label(_('Address to change to')),
+ TextBox(name='change_to'),
+ CheckBox('notice_new', 'yes', 0).Format() +
+ '&nbsp;' +
+ _('Send notice')
+ ])
+ table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
+ table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
+ table.AddCellInfo(table.GetCurrentRowIndex(), 2, bgcolor=GREY)
+ container.AddItem(Center(table))
+
+
+
def password_inputs(mlist):
adminurl = mlist.GetScriptURL('admin', absolute=1)
table = Table(cellspacing=3, cellpadding=4)
@@ -1436,10 +1488,12 @@ def change_options(mlist, category, subcat, cgidata, doc):
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)
+ send_unsub_notifications = safeint(
+ 'send_unsub_notifications_to_list_owner',
+ mlist.admin_notify_mchanges)
+ userack = safeint(
+ 'send_unsub_ack_to_this_batch',
+ mlist.send_goodbye_msg)
unsubscribe_errors = []
unsubscribe_success = []
for addr in names:
@@ -1461,13 +1515,77 @@ def change_options(mlist, category, subcat, cgidata, doc):
color='#ff0000', size='+2')).Format()))
doc.AddItem(UnorderedList(*unsubscribe_errors))
doc.AddItem('<p>')
+ # Address Changes
+ if cgidata.has_key('change_from'):
+ change_from = cgidata.getvalue('change_from', '')
+ change_to = cgidata.getvalue('change_to', '')
+ schange_from = Utils.websafe(change_from)
+ schange_to = Utils.websafe(change_to)
+ success = False
+ msg = None
+ if not (change_from and change_to):
+ msg = _('You must provide both current and new addresses.')
+ elif change_from == change_to:
+ msg = _('Current and new addresses must be different.')
+ elif mlist.isMember(change_to):
+ # ApprovedChangeMemberAddress will just delete the old address
+ # and we don't want that here.
+ msg = _('%(schange_to)s is already a list member.')
+ else:
+ try:
+ Utils.ValidateEmail(change_to)
+ except (Errors.MMBadEmailError, Errors.MMHostileAddress):
+ msg = _('%(schange_to)s is not a valid email address.')
+ if msg:
+ doc.AddItem(Header(3, msg))
+ doc.AddItem('<p>')
+ return
+ try:
+ mlist.ApprovedChangeMemberAddress(change_from, change_to, False)
+ except Errors.NotAMemberError:
+ msg = _('%(schange_from)s is not a member')
+ except Errors.MMAlreadyAMember:
+ msg = _('%(schange_to)s is already a member')
+ except Errors.MembershipIsBanned, pat:
+ spat = Utils.websafe(str(pat))
+ msg = _('%(schange_to)s matches banned pattern %(spat)s')
+ else:
+ msg = _('Address %(schange_from)s changed to %(schange_to)s')
+ success = True
+ doc.AddItem(Header(3, msg))
+ lang = mlist.getMemberLanguage(change_to)
+ otrans = i18n.get_translation()
+ i18n.set_language(lang)
+ list_name = mlist.getListAddress()
+ text = Utils.wrap(_("""The member address %(change_from)s on the
+%(list_name)s list has been changed to %(change_to)s.
+"""))
+ subject = _('%(list_name)s address change notice.')
+ i18n.set_translation(otrans)
+ if success and cgidata.getvalue('notice_old', '') == 'yes':
+ # Send notice to old address.
+ msg = Message.UserNotification(change_from,
+ mlist.GetOwnerEmail(),
+ text=text,
+ subject=subject,
+ lang=lang
+ )
+ msg.send(mlist)
+ doc.AddItem(Header(3, _('Notification sent to %(schange_from)s.')))
+ if success and cgidata.getvalue('notice_new', '') == 'yes':
+ # Send notice to new address.
+ msg = Message.UserNotification(change_to,
+ mlist.GetOwnerEmail(),
+ text=text,
+ subject=subject,
+ lang=lang
+ )
+ msg.send(mlist)
+ doc.AddItem(Header(3, _('Notification sent to %(schange_to)s.')))
+ doc.AddItem('<p>')
# 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
+ val = safeint('allmodbit_val')
if val not in (0, 1):
doc.addError(_('Bad moderation flag value'))
else: