membership management developments: chunkifying

i have somehow managed to get the membership management section chunkified, with proper authentication.
there is a new variable in mm_defaults called DEFAULT_CHUNK_SIZE, which as commented sets the size of the chunk of members presented at a time to admins editing their options/ unsubscribing them.
this section of membership management uses cookie authentication. As Authentication is getting particulary messy in the program cgi/admin, i wouldn't suggest (m)any futher develops that involve special authentication without rewriting the script from the bottom up.
anyway, here is a patch, as applied against the distribution version that i posted about the other day that does an improved job of membership management stuff.
scott
Index: cgi/admin
RCS file: /usr/local/cvsroot/mailman/cgi/admin,v retrieving revision 1.1.1.1 diff -c -r1.1.1.1 admin *** admin 1998/04/21 03:27:57 1.1.1.1 --- admin 1998/04/21 15:45:15
*** 9,17 **** --- 9,20 ----
import sys sys.path.append('/home/mailman/mailman/modules')
# XXX need to fix this, this directory should be configurable.
sys.path.append('/home/mailman/cgi-bin') import os, cgi, string, crypt, types import mm_utils, maillist, mm_cfg, mm_err from htmlformat import *
import Cookie
try: sys.stderr = mm_utils.StampedLogger("error", label = 'admin',
*** 27,32 **** --- 30,38 ---- ('bounce', "Bounce Options"), ('archive', "Archival Options")]
- #
- # XXX this this data is also in mm_mailcmd (i think), it should become shared
- # option_info = { 'digest' : 0, 'nomail' : mm_cfg.DisableDelivery, 'norcv' : mm_cfg.DontReceiveOwnPosts,
*** 36,42 **** }
- def main(): """Process and produce list options form.
--- 42,47 ----
*** 80,86 ****
FormatOptionHelp(doc, cgi_data['VARHELP'].value, list)
print doc.Format(bgcolor="#ffffff")
return
! if not cgi_data.has_key('adminpw'):
AddErrorMessage(doc,
'Error: You must supply the admin password to'
' change options.')
--- 85,125 ----
FormatOptionHelp(doc, cgi_data['VARHELP'].value, list)
print doc.Format(bgcolor="#ffffff")
return
! if cgi_data.has_key('adminpw') and cgi_data.has_key('edit_members'):
! adminpw = cgi_data["adminpw"]
! if type(adminpw) is type([]):
! adminpw = adminpw[0].value # use the first one, i guess -scott cotton
! else:
! adminpw = adminpw.value
! try:
! list.ConfirmAdminPassword(adminpw)
! FormatConfiguration(doc, list, category, category_suffix, cgi_data, 0)
! c = Cookie.Cookie()
! c[list.real_name] = hash(list.real_name)
! print c
! except mm_err.MMBadPasswordError:
! AddErrorMessage(doc, 'Error: Incorrect admin password.')
! FormatConfiguration(doc, list, category, category_suffix, cgi_data, 1)
! print doc.Format(bgcolor="#ffffff")
! return
! elif cgi_data.has_key('chunk'):
! if os.environ.has_key("HTTP_COOKIE"):
! cookie = Cookie.Cookie(os.environ["HTTP_COOKIE"])
! if cookie.has_key(list.real_name):
! if cookie[list.real_name].value == hash(list.real_name):
! FormatConfiguration(doc, list, category, category_suffix, cgi_data, 0)
! print doc.Format(bgcolor="#ffffff")
! return
! else:
! AddErrorMessage(doc, "Error: Invalid Cookie value")
! else:
! AddErrorMessage(doc, "Error, no cookie for list!")
! else:
! AddErrorMessage(doc, "no cookie!")
! FormatConfiguration(doc, list, category, category_suffix, cgi_data)
! print doc.Format(bgcolor="#ffffff")
! return
! elif not cgi_data.has_key('adminpw') :
AddErrorMessage(doc,
'Error: You must supply the admin password to'
' change options.')
*** 90,103 **** adminpw = adminpw[0].value # use the first one, i guess -scott cotton else: adminpw = adminpw.value ! try: list.ConfirmAdminPassword(adminpw) ! ChangeOptions(list, category, cgi_data, doc) # Yuck. This shouldn't need to be here. WTF?!? ! if not list.digestable and not list.nondigestable: list.nondigestable = 1 ! except mm_err.MMBadPasswordError: ! AddErrorMessage(doc, 'Error: Incorrect admin password.')
if not list.digestable and len(list.digest_members):
AddErrorMessage(doc,
--- 129,142 ---- adminpw = adminpw[0].value # use the first one, i guess -scott cotton else: adminpw = adminpw.value ! try: list.ConfirmAdminPassword(adminpw) ! ChangeOptions(list, category, cgi_data, doc) # Yuck. This shouldn't need to be here. WTF?!? ! if not list.digestable and not list.nondigestable: list.nondigestable = 1 ! except mm_err.MMBadPasswordError: ! AddErrorMessage(doc, 'Error: Incorrect admin password.')
if not list.digestable and len(list.digest_members):
AddErrorMessage(doc,
*** 121,127 **** ' (does it have the colon?)<ul> %s </ul>', line)
! FormatConfiguration(doc, list, category, category_suffix, cgi_data) print doc.Format(bgcolor="#ffffff")
finally:
--- 160,169 ---- ' (does it have the colon?)<ul> %s </ul>', line)
! if cgi_data.has_key('edit_member_options'): # passwd already checked
! FormatConfiguration(doc, list, category, category_suffix, cgi_data, 0)
! else:
! FormatConfiguration(doc, list, category, category_suffix, cgi_data)
print doc.Format(bgcolor="#ffffff")
finally:
*** 204,210 ****
print doc.Format(bgcolor="#ffffff")
! def FormatConfiguration(doc, list, category, category_suffix, cgi_data): """Produce the overall doc, *except* any processing error messages.""" for k, v in CATEGORIES: if k == category: label = v --- 246,252 ----
print doc.Format(bgcolor="#ffffff")
! def FormatConfiguration(doc, list, category, category_suffix, cgi_data, front_page=1): """Produce the overall doc, *except* any processing error messages.""" for k, v in CATEGORIES: if k == category: label = v
*** 253,269 **** " bottom. (You can also change your password there," " as well.)<p>")
! form.AddItem(FormatOptionsSection(category, list, cgi_data))
form.AddItem(Center(FormatPasswordStuff()))
form.AddItem(list.GetMailmanFooter())
! def FormatOptionsSection(category, list, cgi_data): """Produce the category-specific options table.""" if category == 'members': # Special case for members section. ! return FormatMembershipOptions(list, cgi_data) options = GetConfigOptions(list, category)
big_table = Table(cellspacing=3, cellpadding=4)
--- 295,311 ---- " bottom. (You can also change your password there," " as well.)<p>")
! form.AddItem(FormatOptionsSection(category, list, cgi_data, front_page))
form.AddItem(Center(FormatPasswordStuff()))
form.AddItem(list.GetMailmanFooter())
! def FormatOptionsSection(category, list, cgi_data, front_page): """Produce the category-specific options table.""" if category == 'members': # Special case for members section. ! return FormatMembershipOptions(list, cgi_data, front_page) options = GetConfigOptions(list, category)
big_table = Table(cellspacing=3, cellpadding=4)
*** 398,404 **** descr = descr + "</div>" return [descr, gui_part]
! def FormatMembershipOptions(list, cgi_data, option_info=option_info): container = Container() header = Table(width="100%") header.AddRow([Center(Header(2, "Membership Management"))]) --- 440,446 ---- descr = descr + "</div>" return [descr, gui_part]
! def FormatMembershipOptions(list, cgi_data, front_page=1, option_info=option_info): container = Container() header = Table(width="100%") header.AddRow([Center(Header(2, "Membership Management"))])
*** 408,428 **** header.AddCellInfo(max(header.GetCurrentRowIndex(), 0), 0, colspan=2, bgcolor ="#FFF0D0") container.AddItem(header) ! if cgi_data.has_key("adminpw"): ! adminpw = cgi_data["adminpw"] ! if type(adminpw) is type([]): # admin password specified more than once ! adminpw = adminpw[0].value # use the first one, i guess ! else: ! adminpw = adminpw.value ! try: ! list.ConfirmAdminPassword(adminpw) ! confirmed = 1 ! except (mm_err.MMBadPasswordError): ! confirmed = 0 ! else: ! confirmed = 0 ! if not cgi_data.has_key('edit_members') or (cgi_data.has_key('edit_members') and ! not confirmed): container.AddItem(Center('\n<h3>To Unsubscribe Members or Edit their Options...</h3>')) container.AddItem(Center(""" Enter the adminitrative password: <input type=password name=adminpw size=10> --- 450,456 ---- header.AddCellInfo(max(header.GetCurrentRowIndex(), 0), 0, colspan=2, bgcolor ="#FFF0D0") container.AddItem(header) ! if front_page: container.AddItem(Center('\n<h3>To Unsubscribe Members or Edit their Options...</h3>')) container.AddItem(Center(""" Enter the adminitrative password: <input type=password name=adminpw size=10>
*** 435,465 **** else: user_table = Table(width="90%") digest_user_table = Table(width="90%") ! user_table.AddRow([Center(Header(4, "Members"))]) user_table.AddCellInfo(user_table.GetCurrentRowIndex(), user_table.GetCurrentCellIndex(), bgcolor="#cccccc", colspan=8)
for member in list.members:
! cells = [member, ! "subscribed " +CheckBox(member + "_subscribed", "on", 1).Format(), ! "digest " + CheckBox(member+"_digest", "off").Format(), ! ] ! for opt in ("hide", "nomail", "ack", "norcv", "plain"): ! if list.GetUserOption(member, option_info[opt]): ! value = "on" ! checked = 1 ! else: ! value = "off" ! checked = 0 ! box = CheckBox("%s_%s" % (member, opt), value, checked) ! cells.append("%s %s" % (opt, box.Format())) ! user_table.AddRow(cells) for member in list.digest_members: ! cells = [member, "subscribed " +CheckBox(member + "_subscribed", "on", 1).Format(),
--- 463,508 ---- else: user_table = Table(width="90%") digest_user_table = Table(width="90%") ! user_table.AddRow([Center(Header(4, "Membership List"))]) user_table.AddCellInfo(user_table.GetCurrentRowIndex(), user_table.GetCurrentCellIndex(), bgcolor="#cccccc", colspan=8)"digest " + CheckBox(member+"_digest", "on", 1).Format(), ] for opt in ("hide", "nomail", "ack", "norcv", "plain"): if list.GetUserOption(member, option_info[opt]): value = "on"
members = {}
! members[member] = 1 for member in list.digest_members: ! digests[member] = 1 ! all = list.members + list.digest_members ! if len(all) > mm_cfg.DEFAULT_CHUNK_SIZE: ! chunks = mm_utils.chunkify(all) ! if not cgi_data.has_key("chunk"): ! chunk = 0 ! else: ! chunk = string.atoi(cgi_data["chunk"].value) ! all = chunks[chunk] ! footer = ("<p><em>To View other sections, click on the appropriate range listed below</em>") ! chunk_indices = range(len(chunks)) ! chunk_indices.remove(chunk) ! buttons = [] ! pi = os.environ["PATH_INFO"] ! for ci in chunk_indices: ! start, end = chunks[ci][0], chunks[ci][-1] ! buttons.append("<a href=/mailman/admin%s?chunk=%d> from %s to %s </a>" % ( pi, ci, start, end)) ! buttons = apply(UnorderedList, tuple(buttons)) ! footer = footer + buttons.Format() + "<p>" ! else: ! all.sort() ! footer = "<p>" ! for member in all: ! cells = [member + "<input type=hidden name=user value=%s>" % (member), "subscribed " +CheckBox(member + "_subscribed", "on", 1).Format(), ]digests = {} for member in list.members:
if members.get(member):
cells.append("digest " + CheckBox(member + "_digest", "off", 0).Format())
else:
cells.append("digest " + CheckBox(member + "_digest", "on", 1).Format()) for opt in ("hide", "nomail", "ack", "norcv", "plain"): if list.GetUserOption(member, option_info[opt]): value = "on"
*** 471,483 ****
cells.append("%s %s" % (opt, box.Format()))
user_table.AddRow(cells)
container.AddItem(Center(user_table))
# trigger member list viewing again and change_options with hidden tags...
# password authentication is still done, so this should be safe
! container.AddItem("<input type=hidden name=edit_member_options value=on>"
! "<input type=hidden name=edit_members value=on><p>")
! container.AddItem("Back to <a href=/mailman/admin/%s/members>Mass Subscribing</a><p>" %
! (list.real_name))
!
return container
def FormatPasswordStuff(): --- 514,529 ---- cells.append("%s %s" % (opt, box.Format())) user_table.AddRow(cells) container.AddItem(Center(user_table))
t = Table(width="90%")
t.AddRow([Center(Header(4, "<a href=/mailman/admin/%s/members>Back to Mass Subscriptions</a>" % list.real_name))])
t.AddCellInfo(t.GetCurrentRowIndex(),
t.GetCurrentCellIndex(),
bgcolor="#cccccc", colspan=8)
container.AddItem(Center(t))
container.AddItem(footer) # trigger member list viewing again and change_options with hidden tags... # password authentication is still done, so this should be safe
! container.AddItem("<input type=hidden name=edit_member_options value=on>") return container
def FormatPasswordStuff():
*** 590,620 **** if getattr(list, property) != value: setattr(list, property, value) dirty = 1
elif cgi_info.has_key('edit_member_options'):
members = []
digest_members = []
org_user_opts = list.user_options.copy()
for member in (list.members + list.digest_members)[:]:
if not cgi_info.has_key(member + "_subscribed"):
list.DeleteMember(member, "cgi: admin/members")
continue
if not cgi_info.has_key(member + "_digest"): # no digest
members.append(member)
else:
digest_members.append(member)
for opt in ("hide", "nomail", "ack", "norcv", "plain"):
if cgi_info.has_key("%s_%s" % (member, opt)):
list.SetUserOption(member, option_info[opt], 1)
else:
list.SetUserOption(member, option_info[opt], 0)
if (list.user_options != org_user_opts):
dirty = 1
if list.members != members:
list.members = members
dirty = 1
if list.digest_members != digest_members:
list.digest_members = digest_members
name_text = cgi_info['subscribees'].value name_text = string.replace(name_text, '\r', '') --- 636,641 ----dirty = 1 elif cgi_info.has_key('subscribees'):
*** 644,649 **** --- 665,705 ---- items = map(lambda x: "%s -- %s" % (x[0], x[1]), subscribe_errors) document.AddItem(apply(UnorderedList, tuple((items)))) document.AddItem("<p>")
elif cgi_info.has_key("user"):
user = cgi_info["user"]
if type(user) is type([]):
users = []
for ui in range(len(user)):
users.append(user[ui].value)
else:
users = [user.value]
for user in users:
if not cgi_info.has_key('%s_subscribed' % (user)):
list.DeleteMember(user)
dirty = 1
continue
if not cgi_info.has_key("%s_digest" % (user)):
if user in list.digest_members:
list.digest_members.remove(user)
dirty = 1
if user not in list.members:
list.members.append(user)
dirty = 1
else:
if user not in list.digest_members:
list.digest_members.append(user)
dirty = 1
if user in list.members:
list.members.remove(user)
dirty = 1
for opt in ("hide", "nomail", "ack", "norcv", "plain"):
if cgi_info.has_key("%s_%s" % (user, opt)):
list.SetUserOption(user, option_info[opt], 1)
dirty = 1
else:
list.SetUserOption(user, option_info[opt], 0)
if cgi_info.has_key('confirmpw'): new = cgi_info['newpw'].valuedirty = 1 if cgi_info.has_key('newpw'):
*** 661,667 **** m = 'Error: You must type in your new password twice.' document.AddItem( Header(3, Italic(FontAttr(m, color="ff5060")))) ! if dirty: list.Save()
--- 717,723 ----
m = 'Error: You must type in your new password twice.'
document.AddItem(
Header(3, Italic(FontAttr(m, color="ff5060"))))
!
if dirty:
list.Save()
Index: modules/mm_defaults.py
RCS file: /usr/local/cvsroot/mailman/modules/mm_defaults.py,v retrieving revision 1.1.1.1 diff -c -r1.1.1.1 mm_defaults.py *** mm_defaults.py 1998/04/21 03:27:58 1.1.1.1 --- mm_defaults.py 1998/04/21 15:52:05
*** 37,42 **** --- 37,48 ---- HOME_PAGE = 'index.html' MAILMAN_OWNER = 'mailman-owner@%s' % DEFAULT_HOST_NAME
- #
- # break up presentation of editing member options under admin/listname/members
- # into chunks of this size.
- #
- DEFAULT_CHUNK_SIZE=20
- # System ceiling on number of batches into which deliveries are divided: MAX_SPAWNS = 40
Index: modules/mm_utils.py
RCS file: /usr/local/cvsroot/mailman/modules/mm_utils.py,v retrieving revision 1.1.1.1 diff -c -r1.1.1.1 mm_utils.py *** mm_utils.py 1998/04/21 03:27:58 1.1.1.1 --- mm_utils.py 1998/04/21 08:27:54
*** 394,400 **** --- 394,418 ---- Logger.write(self, l)
def chunkify(members, chunksize=mm_cfg.DEFAULT_CHUNK_SIZE):
"""
return a list of lists of members
"""
members.sort()
res = []
while 1:
if not members:
break
chunk = members[:chunksize]
res.append(chunk)
members = members[chunksize:]
return res
participants (1)
-
Scott