[Mailman-Developers] a patch to scale Cgi/admin.py

Fil fil at rezo.net
Mon Oct 31 01:19:02 CET 2005


> The links to a specific chunk would be styled as
>         /members/?start=jane at doe.com
> 
> Not sure if my English makes sense, I'll just post the code when I'm done
> pythonizing the idea. I don't even know how to compare two strings in
> python, so it might take a little while :-D

Okay, now it's done -- it's just a functionality rewrite, nothing is lost
except a few lines of code :)

Enclosed is the patch + the patched file.
(I'm not fully in sync with the CVS as I got an error upgrading from 2.1.6b
to 2.1-Maint)

If you want to try it it's simple and can't do much harm, as it's only
affecting the Web GUI - you don't have to restart Mailman, just save
Mailman/Cgi/admin.py aside (in case), and replace it with this one.

Note that I also removed the annoying "language" menu when there's only one
language available.

(BTW Something I'd like to add is a 'title="jane at doe.com"' attribute in the
<a href> element, but I couldn't find how to do it.)

                                 * * *

We'll still need to solve the "search" issue, but that will require much
more work, I think, as the best way to do it will be to implement a new
method in the memberadaptor; and that will need discussion, as there are two
options:
- 1) add a "getMembersMatching(regexp) method  (best, I think, as it can
     leverage foreign search methods, i.e. MySQL's "SELECT WHERE name LIKE %s")
- 2) add a "getMembersWithNames()" method (not so good, but for the sake oif
     the discussion I include the idea here)
Please tell me which route to take, or I'll take Route 1.

-- Fil

-------------- next part --------------
A non-text attachment was scrubbed...
Name: mailman_Cgi_admin.py
Type: text/x-python
Size: 62819 bytes
Desc: not available
Url : http://mail.python.org/pipermail/mailman-developers/attachments/20051031/2137b527/mailman_Cgi_admin-0001.py
-------------- next part --------------
--- Mailman/Cgi/admin.py	2005-02-12 21:22:55.000000000 +0100
+++ /var/local/mailman/Mailman/Cgi/admin.py	2005-10-31 00:57:05.000000000 +0100
@@ -28,6 +28,7 @@ import urllib
 import signal
 from types import *
 from string import lowercase, digits
+from urllib import urlencode
 
 from email.Utils import unquote, parseaddr, formataddr
 
@@ -47,7 +48,6 @@ _ = i18n._
 i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
 
 NL = '\n'
-OPTCOLUMNS = 11
 
 try:
     True, False
@@ -876,93 +876,97 @@ def membership_options(mlist, subcat, cg
             doc.addError(_('Bad regular expression: ') + regexp)
         else:
             # BAW: There's got to be a more efficient way of doing this!
+            # yes please... this doesn't scale at all
             names = [mlist.getMemberName(s) or '' for s in all]
             all = [a for n, a in zip(names, all)
                    if cre.search(n) or cre.search(a)]
-    chunkindex = None
-    bucket = None
-    actionurl = None
+
+    # Do we want to display the language menu?
+    langs = mlist.GetAvailableLanguages()
+    if len(langs) > 1:
+        langdescs = [_(Utils.GetLanguageDescr(lang)) for lang in langs]
+        OPTCOLUMNS = 11
+    else:
+        langs = None
+        OPTCOLUMNS = 10
+    options = [_('unsub'), _('member address<br>member name'),
+               _('mod'), _('hide'), _('nomail<br>[reason]'), _('ack'),
+               _('not metoo'), _('nodupes'), _('digest'), _('plain')]
+    if langs:
+        options.append(_('language'))
+
+    starters = []
+
+    # List all members that we want to display
+    # maybe everyone if they are not too many
     if len(all) < chunksz:
         members = all
     else:
-        # Split them up alphabetically, and then split the alphabetical
-        # listing by chunks
-        buckets = {}
-        for addr in all:
-            members = buckets.setdefault(addr[0].lower(), [])
-            members.append(addr)
-        # Now figure out which bucket we want
-        bucket = None
-        qs = {}
-        # POST methods, even if their actions have a query string, don't get
-        # put into FieldStorage's keys :-(
+        # Retrieve the start address
+        # BAW: POST methods, even if their actions have a query string, don't
+        # get put into FieldStorage's keys :-(
+        start = ''
         qsenviron = os.environ.get('QUERY_STRING')
         if qsenviron:
             qs = cgi.parse_qs(qsenviron)
-            bucket = qs.get('letter', 'a')[0].lower()
-            if bucket not in digits + lowercase:
-                bucket = None
-        if not bucket or not buckets.has_key(bucket):
-            keys = buckets.keys()
-            keys.sort()
-            bucket = keys[0]
-        members = buckets[bucket]
-        action = adminurl + '/members?letter=%s' % bucket
-        if len(members) <= chunksz:
-            form.set_action(action)
-        else:
-            i, r = divmod(len(members), chunksz)
-            numchunks = i + (not not r * 1)
-            # Now chunk them up
-            chunkindex = 0
-            if qs.has_key('chunk'):
-                try:
-                    chunkindex = int(qs['chunk'][0])
-                except ValueError:
-                    chunkindex = 0
-                if chunkindex < 0 or chunkindex > numchunks:
-                    chunkindex = 0
-            members = members[chunkindex*chunksz:(chunkindex+1)*chunksz]
-            # And set the action URL
-            form.set_action(action + '&chunk=%s' % chunkindex)
-    # So now members holds all the addresses we're going to display
-    allcnt = len(all)
-    if bucket:
-        membercnt = len(members)
-        usertable.AddRow([Center(Italic(_(
-            '%(allcnt)s members total, %(membercnt)s shown')))])
-    else:
-        usertable.AddRow([Center(Italic(_('%(allcnt)s members total')))])
-    usertable.AddCellInfo(usertable.GetCurrentRowIndex(),
+            if qs.has_key('start'):
+                start = qs.get('start')[0].lower()
+
+        # Show start links for every address that is either starting a bucket
+        # or, inside the current bucket, starting a chunk
+        num = 0
+        numtaken = 0
+        members = []
+        if all:
+            bucket = all[0][0].upper() # first bucket, ignore
+        try:
+            currentbucket = start[0].upper()
+        except:
+            currentbucket = ''
+
+        for addr in all:
+            # If the address changes of bucket, or is in the same bucket
+            # as the start parameter, and a multiple of chunksize addresses
+            # write a link to it
+            reason = ''
+            if addr[0].upper() != bucket:
+                bucket = addr[0].upper()
+                reason = '<b>' + bucket + '</b>'
+                num = 0
+            elif num % chunksz == 0 and bucket == currentbucket:
+                reason = '<small>' + str(num/chunksz) + '</small>'
+            if reason:
+                if start == addr:
+                    link = reason
+                else:
+                    url = adminurl + '/members?' + urlencode({'start':addr})
+                    link = Link(url, reason).Format()
+                starters.append(link)
+            num += 1
+
+            # If the address is after the START value, take it for display
+            # but don't take more than the max chunksize
+            if addr.lower() >= start and numtaken < chunksz:
+                numtaken += 1
+                members.append(addr)
+
+
+    # Add the start links
+    if starters:
+        joiner = '&nbsp;' + '\n'
+        usertable.AddRow([Center(joiner.join(starters))])
+        usertable.AddCellInfo(usertable.GetCurrentRowIndex(),
                           usertable.GetCurrentCellIndex(),
                           colspan=OPTCOLUMNS,
                           bgcolor=mm_cfg.WEB_ADMINITEM_COLOR)
-    # Add the alphabetical links
-    if bucket:
-        cells = []
-        for letter in digits + lowercase:
-            if not buckets.get(letter):
-                continue
-            url = adminurl + '/members?letter=%s' % letter
-            if letter == bucket:
-                show = Bold('[%s]' % letter.upper()).Format()
-            else:
-                show = letter.upper()
-            cells.append(Link(url, show).Format())
-        joiner = '&nbsp;'*2 + '\n'
-        usertable.AddRow([Center(joiner.join(cells))])
+
+    # So now members holds all the addresses we're going to display
+    usertable.AddRow('')
     usertable.AddCellInfo(usertable.GetCurrentRowIndex(),
                           usertable.GetCurrentCellIndex(),
                           colspan=OPTCOLUMNS,
                           bgcolor=mm_cfg.WEB_ADMINITEM_COLOR)
-    usertable.AddRow([Center(h) for h in (_('unsub'),
-                                          _('member address<br>member name'),
-                                          _('mod'), _('hide'),
-                                          _('nomail<br>[reason]'),
-                                          _('ack'), _('not metoo'),
-                                          _('nodupes'),
-                                          _('digest'), _('plain'),
-                                          _('language'))])
+    usertable.AddRow([Center(h) for h in options])
     rowindex = usertable.GetCurrentRowIndex()
     for i in range(OPTCOLUMNS):
         usertable.AddCellInfo(rowindex, i, bgcolor=mm_cfg.WEB_ADMINITEM_COLOR)
@@ -978,6 +982,8 @@ def membership_options(mlist, subcat, cg
                   MemberAdaptor.BYADMIN : _('A'),
                   MemberAdaptor.BYBOUNCE: _('B'),
                   }
+    # memorize the regular-or-digest list
+    regular_or_digest = mlist.getRegularMemberKeys()
     # Now populate the rows
     for addr in members:
         link = Link(mlist.GetOptionsURL(addr, obscure=1),
@@ -1021,8 +1027,9 @@ def membership_options(mlist, subcat, cg
         # This code is less efficient than the original which did a has_key on
         # the underlying dictionary attribute.  This version is slower and
         # less memory efficient.  It points to a new MemberAdaptor interface
-        # method.
-        if addr in mlist.getRegularMemberKeys():
+        # method. (Modified by Fil to "cache" the result - useful for
+        # MySQLMemberAdaptor)
+        if addr in regular_or_digest:
             cells.append(Center(CheckBox(addr + '_digest', 'off', 0).Format()))
         else:
             cells.append(Center(CheckBox(addr + '_digest', 'on', 1).Format()))
@@ -1035,13 +1042,12 @@ def membership_options(mlist, subcat, cg
         cells.append(Center(CheckBox('%s_plain' % addr, value, checked)))
         # User's preferred language
         langpref = mlist.getMemberLanguage(addr)
-        langs = mlist.GetAvailableLanguages()
-        langdescs = [_(Utils.GetLanguageDescr(lang)) for lang in langs]
-        try:
-            selected = langs.index(langpref)
-        except ValueError:
-            selected = 0
-        cells.append(Center(SelectOptions(addr + '_language', langs,
+        if langs:
+            try:
+                selected = langs.index(langpref)
+            except ValueError:
+                selected = 0
+            cells.append(Center(SelectOptions(addr + '_language', langs,
                                           langdescs, selected)).Format())
         usertable.AddRow(cells)
     # Add the usertable and a legend
@@ -1105,23 +1111,6 @@ def membership_options(mlist, subcat, cg
                  _('Click here to include the legend for this table.')))
     container.AddItem(Center(usertable))
 
-    # There may be additional chunks
-    if chunkindex is not None:
-        buttons = []
-        url = adminurl + '/members?%sletter=%s&' % (addlegend, bucket)
-        footer = _('''<p><em>To view more members, click on the appropriate
-        range listed below:</em>''')
-        chunkmembers = buckets[bucket]
-        last = len(chunkmembers)
-        for i in range(numchunks):
-            if i == chunkindex:
-                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'))
-            buttons.append(link)
-        buttons = UnorderedList(*buttons)
-        container.AddItem(footer + buttons.Format() + '<p>')
     return container
 
 


More information about the Mailman-Developers mailing list