summaryrefslogtreecommitdiff
path: root/Mailman
diff options
context:
space:
mode:
authorbwarsaw2001-07-19 01:59:40 +0000
committerbwarsaw2001-07-19 01:59:40 +0000
commit02eb416f1a0d4290198c2a4e819b8c962f50a2bf (patch)
tree08c47ae23ed6a2f710ce108d9a3e540524e6970c /Mailman
parent429107ec0b6c5f3d7f379de3dfeeba1031652fc5 (diff)
downloadmailman-02eb416f1a0d4290198c2a4e819b8c962f50a2bf.tar.gz
mailman-02eb416f1a0d4290198c2a4e819b8c962f50a2bf.tar.zst
mailman-02eb416f1a0d4290198c2a4e819b8c962f50a2bf.zip
A revamp to use the new membership API, and to be able to set/get
member's real names. Specifically, main(): IsMember() -> isMember(); FindUser()/GetUserSubscribedAddress() -> getMemberCPAddress() GetPreferredLanguage() -> getMemberLanguage() Support the printing of topic details when they click on the "Details" link after the topic name (works like admin interface's VARHELP). In the change-of-address stanza, add a check for the fullname field, which contains the member's real name. After extracting the fullname, new-address, and confirm-address fields, be more careful about including error messages, so that if the real name field is set or cleared, we ignore the address fields if they are both blank. Handle setting the member's name globally, just like a global change of address. DeleteMember() -> ApprovedDeleteMember() Add recognition of the ReceiveNonmatchingTopics flag to determine what the user wants for messages that match no topics (they can either receive them or ignore them). GetUserOption() -> getMemberOption() Add processing of user topic selections. SetUserOption() -> setMemberOption() and don't special case when setting digest deliver; setMemberOption() handles that properly. options_page(): Include the user's full name in presentable_user, as in "john.doe@dom.ain, John Doe". If there's no user name then just print the email address as before. "Change My Address" button -> "Change My Address and Name" Add replacements for the fullname box, and the topics option. IsMember() -> isMember() ChangeUserPassword() -> setMemberPassword() SetUserOption() -> setMemberOption() topic_details(): New method to include the topic details in the top information area.
Diffstat (limited to 'Mailman')
-rw-r--r--Mailman/Cgi/options.py236
1 files changed, 187 insertions, 49 deletions
diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py
index 3aa2ac623..e13442e84 100644
--- a/Mailman/Cgi/options.py
+++ b/Mailman/Cgi/options.py
@@ -19,6 +19,8 @@
import os
import cgi
import signal
+import urllib
+from types import ListType
from Mailman import mm_cfg
from Mailman import Utils
@@ -76,7 +78,7 @@ def main():
# Sanity check the user
user = Utils.UnobscureEmail(SLASH.join(parts[1:]))
user = Utils.LCDomain(user)
- if not mlist.IsMember(user):
+ if not mlist.isMember(user):
realname = mlist.real_name
title = _('CGI script error')
doc.SetTitle(title)
@@ -89,19 +91,35 @@ def main():
return
# Find the case preserved email address (the one the user subscribed with)
- lcuser = mlist.FindUser(user)
- cpuser = mlist.GetUserSubscribedAddress(lcuser)
+ lcuser = user.lower()
+ cpuser = mlist.getMemberCPAddress(lcuser)
if lcuser == cpuser:
cpuser = None
+ # The total contents of the user's response
+ cgidata = cgi.FieldStorage()
+
# And now we know the user making the request, so set things up to for the
# user's stored preferred language, overridden by any form settings for
# their new language preference.
- cgidata = cgi.FieldStorage()
- userlang = cgidata.getvalue('language', mlist.GetPreferredLanguage(user))
+ userlang = cgidata.getvalue('language', mlist.getMemberLanguage(user))
doc.set_language(userlang)
i18n.set_language(userlang)
+ # See if this is VARHELP on topics.
+ 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:
+ topic_details(mlist, doc, user, cpuser, userlang, varhelp)
+ return
+
# Are we processing an unsubscription request from the login screen?
if cgidata.has_key('login-unsub'):
# Because they can't supply a password for unsubscribing, we'll need
@@ -183,18 +201,44 @@ def main():
return
if cgidata.has_key('change-of-address'):
+ # We could be changing the user's full name, email address, or both
+ membername = cgidata.getvalue('fullname')
newaddr = cgidata.getvalue('new-address')
confirmaddr = cgidata.getvalue('confirm-address')
- if not newaddr or not confirmaddr:
+
+ oldname = mlist.getMemberName(user)
+ set_address = set_membername = 0
+ # We will change the member's name under the following conditions:
+ # - membername has a value
+ # - membername has no value, but they /used/ to have a membername
+ if membername and membername <> oldname:
+ # Setting it to a new value
+ set_membername = 1
+ if not membername and oldname:
+ # Unsetting it
+ set_membername = 1
+ # We will change the user's address if both newaddr and confirmaddr
+ # are non-blank, have the same value, and aren't the currently
+ # subscribed email address (when compared case-sensitively). If both
+ # are blank, but membername is set, we ignore it, otherwise we print
+ # an error.
+ if newaddr and confirmaddr:
+ if newaddr <> confirmaddr:
+ options_page(mlist, doc, user, cpuser, userlang,
+ _('Addresses did not match!'))
+ print doc.Format()
+ return
+ if newaddr == user:
+ options_page(mlist, doc, user, cpuser, userlang,
+ _('You are already using that email address'))
+ print doc.Format()
+ return
+ set_address = 1
+ elif (newaddr or confirmaddr) and not set_membername:
options_page(mlist, doc, user, cpuser, userlang,
_('Addresses may not be blank'))
print doc.Format()
return
- if newaddr <> confirmaddr:
- options_page(mlist, doc, user, cpuser, userlang,
- _('Addresses did not match!'))
- print doc.Format()
- return
# See if the user wants to change their email address globally
globally = cgidata.getvalue('changeaddr-globally')
@@ -204,22 +248,33 @@ def main():
mlist.Unlock()
sys.exit(0)
- # Register the pending change after the list is locked
- msg = _('A confirmation message has been sent to %(newaddr)s')
- mlist.Lock()
- try:
+ signal.signal(signal.SIGTERM, sigterm_handler)
+ msg = ''
+ if set_address:
+ # Register the pending change after the list is locked
+ msg = _('A confirmation message has been sent to %(newaddr)s')
+ mlist.Lock()
+ try:
+ try:
+ mlist.ChangeMemberAddress(user, newaddr, globally)
+ mlist.Save()
+ finally:
+ mlist.Unlock()
+ except Errors.MMBadEmailError:
+ msg = _('Bad email address provided')
+ except Errors.MMHostileAddress:
+ msg = _('Illegal email address provided')
+ except Errors.MMAlreadyAMember:
+ msg = _('%(newaddr)s is already a member of the list.')
+
+ if set_membername:
+ mlist.Lock()
try:
- signal.signal(signal.SIGTERM, sigterm_handler)
- mlist.ChangeMemberAddress(user, newaddr, globally)
+ mlist.ChangeMemberName(user, membername, globally)
mlist.Save()
finally:
mlist.Unlock()
- except Errors.MMBadEmailError:
- msg = _('Bad email address provided')
- except Errors.MMHostileAddress:
- msg = _('Illegal email address provided')
- except Errors.MMAlreadyAMember:
- msg = _('%(newaddr)s is already a member of the list.')
+ msg += _('Member name successfully changed.')
options_page(mlist, doc, user, cpuser, userlang, msg)
print doc.Format()
@@ -274,11 +329,11 @@ def main():
# Okay, zap them. Leave them sitting at the list's listinfo page. We
# must own the list lock, and we want to make sure the user (BAW: and
# list admin?) is informed of the removal.
+ signal.signal(signal.SIGTERM, sigterm_handler)
mlist.Lock()
try:
- signal.signal(signal.SIGTERM, sigterm_handler)
- mlist.DeleteMember(user, _('via the member options page'),
- admin_notif=1, userack=1)
+ mlist.ApprovedDeleteMember(user, _('via the member options page'),
+ admin_notif=1, userack=1)
mlist.Save()
finally:
mlist.Unlock()
@@ -316,6 +371,7 @@ def main():
('disablemail', mm_cfg.DisableDelivery),
('conceal', mm_cfg.ConcealSubscription),
('remind', mm_cfg.SuppressPasswordReminder),
+ ('rcvtopic', mm_cfg.ReceiveNonmatchingTopics),
):
try:
newval = int(cgidata.getvalue(item))
@@ -323,7 +379,7 @@ def main():
newval = None
# Skip this option if there was a problem or it wasn't changed
- if newval is None or newval == mlist.GetUserOption(user, flag):
+ if newval is None or newval == mlist.getMemberOption(user, flag):
continue
newvals.append((flag, newval))
@@ -334,6 +390,20 @@ def main():
else:
newvals.append((SETLANGUAGE, userlang))
+ # Process user selected topics, but don't make the changes to the
+ # MailList object; we must do that down below when the list is
+ # locked.
+ topicnames = cgidata.getvalue('usertopic')
+ if topicnames:
+ # Some topics were selected. topicnames can actually be a string
+ # or a list of strings depending on whether more than one topic
+ # was selected or not.
+ if not isinstance(topicnames, ListType):
+ # Assume it was a bare string, so listify it
+ topicnames = [topicnames]
+ # unquote the topic names
+ topicnames = [urllib.unquote_plus(n) for n in topicnames]
+
# The standard sigterm handler (see above)
def sigterm_handler(signum, frame, mlist=mlist):
mlist.Unlock()
@@ -350,22 +420,15 @@ def main():
mlist.SetPreferredLanguage(user, newval)
continue
- mlist.SetUserOption(user, flag, newval, save_list=0)
+ mlist.setMemberOption(user, flag, newval)
+
+ # Set the topics information
+ # FIXME: should use MemberAdaptor API
+ if topicnames:
+ mlist.topics_userinterest[user] = topicnames
+ elif mlist.topics_userinterest.has_key(user):
+ del mlist.topics_userinterest[user]
- # Digests also need another setting, which is a bit bogus, as
- # SetUserOption() should be taught about this special step.
- if flag == mm_cfg.Digests:
- try:
- mlist.SetUserDigest(user, newval)
- if newval == 0:
- digestwarn = 1
- except (Errors.MMAlreadyDigested,
- Errors.MMAlreadyUndigested):
- pass
- except Errors.MMCantDigestError:
- cantdigest = 1
- except Errors.MMMustDigestError:
- mustdigest = 1
# All done
mlist.Save()
finally:
@@ -430,6 +493,10 @@ def options_page(mlist, doc, user, cpuser, userlang, message=''):
else:
presentable_user = user
+ name = mlist.getMemberName(user)
+ if name:
+ presentable_user += ', %s' % name
+
# Do replacements
replacements = mlist.GetStandardReplacements(userlang)
replacements['<mm-results>'] = Bold(FontSize('+1', message)).Format()
@@ -503,10 +570,41 @@ def options_page(mlist, doc, user, cpuser, userlang, message=''):
replacements['<mm-confirm-address-box>'] = mlist.FormatBox(
'confirm-address')
replacements['<mm-change-address-button>'] = mlist.FormatButton(
- 'change-of-address', _('Change My Address'))
+ 'change-of-address', _('Change My Address and Name'))
replacements['<mm-global-change-of-address>'] = CheckBox(
'changeaddr-globally', 1, checked=0).Format()
+ membername = mlist.getMemberName(user)
+ if membername is None:
+ membername = ''
+ replacements['<mm-fullname-box>'] = mlist.FormatBox('fullname',
+ value=membername)
+
+ # Create the topics radios. BAW: what if the list admin deletes a topic,
+ # but the user still wants to get that topic message?
+ usertopics = mlist.topics_userinterest.get(user, [])
+ if mlist.topics:
+ table = Table(border="0")
+ for name, pattern, description, emptyflag in mlist.topics:
+ quotedname = urllib.quote_plus(name)
+ details = Link(mlist.GetScriptURL('options') +
+ '/%s/?VARHELP=%s' % (user, quotedname),
+ ' (Details)')
+ if name in usertopics:
+ checked = 1
+ else:
+ checked = 0
+ table.AddRow([CheckBox('usertopic', quotedname, checked=checked),
+ name + details.Format()])
+ topicsfield = table.Format()
+ else:
+ topicsfield = _('<em>No topics defined</em>')
+ replacements['<mm-topics>'] = topicsfield
+ replacements['<mm-suppress-nonmatching-topics>'] = (
+ mlist.FormatOptionButton(mm_cfg.ReceiveNonmatchingTopics, 0, user))
+ replacements['<mm-receive-nonmatching-topics>'] = (
+ mlist.FormatOptionButton(mm_cfg.ReceiveNonmatchingTopics, 1, user))
+
if cpuser is not None:
replacements['<mm-case-preserved-user>'] = _('''
You are subscribed to this list with the case-preserved address
@@ -602,7 +700,7 @@ def lists_of_member(hostname, user):
mlist = MailList.MailList(listname, lock=0)
if mlist.host_name <> hostname:
continue
- if not mlist.IsMember(user):
+ if not mlist.isMember(user):
continue
onlists.append(mlist)
return onlists
@@ -626,8 +724,10 @@ def change_password(mlist, user, newpw, confirmpw):
try:
# Install the emergency shutdown signal handler
signal.signal(signal.SIGTERM, sigterm_handler)
- # change the user's password
- mlist.ChangeUserPassword(user, newpw, confirmpw)
+ # change the user's password. The password must already have been
+ # compared to the confirmpw and otherwise been vetted for
+ # acceptability.
+ mlist.setMemberPassword(user, newpw)
mlist.Save()
finally:
mlist.Unlock()
@@ -650,12 +750,50 @@ def global_options(mlist, user, global_enable, global_remind):
signal.signal(signal.SIGTERM, sigterm_handler)
if global_enable is not None:
- mlist.SetUserOption(user, mm_cfg.DisableDelivery, global_enable)
+ mlist.setMemberOption(user, mm_cfg.DisableDelivery, global_enable)
if global_remind is not None:
- mlist.SetUserOption(user, mm_cfg.SuppressPasswordReminder,
- global_remind)
+ mlist.setMemberOption(user, mm_cfg.SuppressPasswordReminder,
+ global_remind)
mlist.Save()
finally:
mlist.Unlock()
+
+
+
+def topic_details(mlist, doc, user, cpuser, userlang, varhelp):
+ # Find out which topic the user wants to get details of
+ reflist = varhelp.split('/')
+ name = None
+ topicname = _('<missing>')
+ if len(reflist) == 1:
+ topicname = urllib.unquote_plus(reflist[0])
+ for name, pattern, description, emptyflag in mlist.topics:
+ if name == topicname:
+ break
+ else:
+ name = None
+
+ if not name:
+ options_page(mlist, doc, user, cpuser, userlang,
+ _('Requested topic is not valid: %(topicname)s'))
+ print doc.Format()
+ return
+
+ table = Table(border=3, width='100%')
+ table.AddRow([Center(Bold(_('Topic filter details')))])
+ table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
+ bgcolor=mm_cfg.WEB_SUBHEADER_COLOR)
+ table.AddRow([Bold(Label(_('Name:'))),
+ Utils.QuoteHyperChars(name)])
+ table.AddRow([Bold(Label(_('Pattern (as regexp):'))),
+ '<pre>' + Utils.QuoteHyperChars(pattern) + '</pre>'])
+ table.AddRow([Bold(Label(_('Description:'))),
+ Utils.QuoteHyperChars(description)])
+ # Make colors look nice
+ for row in range(1, 4):
+ table.AddCellInfo(row, 0, bgcolor=mm_cfg.WEB_ADMINITEM_COLOR)
+
+ options_page(mlist, doc, user, cpuser, userlang, table.Format())
+ print doc.Format()