Code

Pagination of index pages.
authorgmcm <gmcm@57a73879-2fb5-44c3-a270-3262357dd7e2>
Mon, 8 Jul 2002 15:32:06 +0000 (15:32 +0000)
committergmcm <gmcm@57a73879-2fb5-44c3-a270-3262357dd7e2>
Mon, 8 Jul 2002 15:32:06 +0000 (15:32 +0000)
New search form.

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@835 57a73879-2fb5-44c3-a270-3262357dd7e2

roundup/cgi_client.py
roundup/htmltemplate.py

index bc6ff1bf11505fdd77ffa541a97988f78794ec73..83380f6685b580444de4784ad87bca0fe29279d6 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: cgi_client.py,v 1.132 2002-07-08 07:26:14 richard Exp $
+# $Id: cgi_client.py,v 1.133 2002-07-08 15:32:05 gmcm Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -133,6 +133,7 @@ function help_window(helpurl, width, height) {
         d[':group'] = ','.join(map(urllib.quote, spec['GROUP']))
         d[':filter'] = ','.join(map(urllib.quote, spec['FILTER']))
         d[':columns'] = ','.join(map(urllib.quote, spec['COLUMNS']))
+        d[':pagesize'] = spec.get('PAGESIZE','50')
 
         # snarf the filterspec
         filterspec = spec['FILTERSPEC'].copy()
@@ -257,7 +258,7 @@ function help_window(helpurl, width, height) {
 <tr class="location-bar">
 <td align=left>%(links)s</td>
 <td align=right>%(user_info)s</td>
-</table>
+</table><br>
 ''')%locals())
 
     def pagefoot(self):
@@ -306,34 +307,67 @@ function help_window(helpurl, width, height) {
             return arg.value.split(',')
         return []
 
+    def index_sort(self):
+        # first try query string
+        x = self.index_arg(':sort')
+        if x:
+            return x
+        # nope - get the specs out of the form
+        specs = []
+        for colnm in self.db.getclass(self.classname).getprops().keys():
+            desc = ''
+            try:
+                spec = self.form[':%s_ss' % colnm]
+            except KeyError:
+                continue
+            spec = spec.value
+            if spec:
+                if spec[-1] == '-':
+                    desc='-'
+                    spec = spec[0]
+                specs.append((int(spec), colnm, desc))
+        specs.sort()
+        x = []
+        for _, colnm, desc in specs:
+            x.append('%s%s' % (desc, colnm))
+        return x
+    
     def index_filterspec(self, filter):
         ''' pull the index filter spec from the form
 
         Links and multilinks want to be lists - the rest are straight
         strings.
         '''
-        props = self.db.classes[self.classname].getprops()
-        # all the form args not starting with ':' are filters
         filterspec = {}
-        for key in self.form.keys():
-            if key[0] == ':': continue
-            if not props.has_key(key): continue
-            if key not in filter: continue
-            prop = props[key]
-            value = self.form[key]
-            if (isinstance(prop, hyperdb.Link) or
-                    isinstance(prop, hyperdb.Multilink)):
-                if type(value) == type([]):
-                    value = [arg.value for arg in value]
+        props = self.db.classes[self.classname].getprops()
+        for colnm in filter:
+            widget = ':%s_fs' % colnm
+            try:
+                val = self.form[widget]
+            except KeyError:
+                try:
+                    val = self.form[colnm]
+                except KeyError:
+                    # they checked the filter box but didn't enter a value
+                    continue
+            propdescr = props.get(colnm, None)
+            if propdescr is None:
+                print "huh? %r is in filter & form, but not in Class!" % colnm
+                raise "butthead programmer"
+            if (isinstance(propdescr, hyperdb.Link) or
+                isinstance(propdescr, hyperdb.Multilink)):
+                if type(val) == type([]):
+                    val = [arg.value for arg in val]
                 else:
-                    value = value.value.split(',')
-                l = filterspec.get(key, [])
-                l = l + value
-                filterspec[key] = l
+                    val = val.value.split(',')
+                l = filterspec.get(colnm, [])
+                l = l + val
+                filterspec[colnm] = l
             else:
-                filterspec[key] = value.value
+                filterspec[colnm] = val.value
+            
         return filterspec
-
+    
     def customization_widget(self):
         ''' The customization widget is visible by default. The widget
             visibility is remembered by show_customization.  Visibility
@@ -355,11 +389,12 @@ function help_window(helpurl, width, height) {
     default_index_filter = ['status']
     default_index_columns = ['id','activity','title','status','assignedto']
     default_index_filterspec = {'status': ['1', '2', '3', '4', '5', '6', '7']}
+    default_pagesize = '50'
 
     def _get_customisation_info(self):
         # see if the web has supplied us with any customisation info
         defaults = 1
-        for key in ':sort', ':group', ':filter', ':columns':
+        for key in ':sort', ':group', ':filter', ':columns', ':pagesize':
             if self.form.has_key(key):
                 defaults = 0
                 break
@@ -373,6 +408,7 @@ function help_window(helpurl, width, height) {
                 filter = d['FILTER']
                 columns = d['COLUMNS']
                 filterspec = d['FILTERSPEC']
+                pagesize = d.get('PAGESIZE', '50')
 
             else:
                 # nope - fall back on the old way of doing it
@@ -382,43 +418,56 @@ function help_window(helpurl, width, height) {
                 filter = self.default_index_filter
                 columns = self.default_index_columns
                 filterspec = self.default_index_filterspec
+                pagesize = self.default_pagesize
         else:
             # make list() extract the info from the CGI environ
             self.classname = 'issue'
-            sort = group = filter = columns = filterspec = None
-        return columns, filter, group, sort, filterspec
+            sort = group = filter = columns = filterspec = pagesize = None
+        return columns, filter, group, sort, filterspec, pagesize
 
     def index(self):
         ''' put up an index - no class specified
         '''
-        columns, filter, group, sort, filterspec = \
+        columns, filter, group, sort, filterspec, pagesize = \
             self._get_customisation_info()
         return self.list(columns=columns, filter=filter, group=group,
-            sort=sort, filterspec=filterspec)
+            sort=sort, filterspec=filterspec, pagesize=pagesize)
 
     def searchnode(self):
-        columns, filter, group, sort, filterspec = \
+        columns, filter, group, sort, filterspec, pagesize = \
             self._get_customisation_info()
-        show_nodes = 1
-        if len(self.form.keys()) == 0:
-            # get the default search filters from instance_config
-            if hasattr(self.instance, 'SEARCH_FILTERS'):
-                for f in self.instance.SEARCH_FILTERS:
-                    spec = getattr(self.instance, f)
-                    if spec['CLASS'] == self.classname:
-                        filter = spec['FILTER']
-                
-            show_nodes = 0
-            show_customization = 1
-        return self.list(columns=columns, filter=filter, group=group,
-            sort=sort, filterspec=filterspec,
-            show_customization=show_customization, show_nodes=show_nodes)
-        
+##        show_nodes = 1
+##        if len(self.form.keys()) == 0:
+##            # get the default search filters from instance_config
+##            if hasattr(self.instance, 'SEARCH_FILTERS'):
+##                for f in self.instance.SEARCH_FILTERS:
+##                    spec = getattr(self.instance, f)
+##                    if spec['CLASS'] == self.classname:
+##                        filter = spec['FILTER']
+##                
+##            show_nodes = 0
+##            show_customization = 1
+##        return self.list(columns=columns, filter=filter, group=group,
+##            sort=sort, filterspec=filterspec,
+##            show_customization=show_customization, show_nodes=show_nodes,
+##            pagesize=pagesize)
+        cn = self.classname
+        self.pagehead(_('%(instancename)s: Index of %(classname)s')%{
+            'classname': cn, 'instancename': self.instance.INSTANCE_NAME})
+        index = htmltemplate.IndexTemplate(self, self.instance.TEMPLATES, cn)
+        self.write('<form onSubmit="return submit_once()" action="%s">\n'%self.classname)
+        all_columns = self.db.getclass(cn).getprops().keys()
+        all_columns.sort()
+        index.filter_section('', filter, columns, group, all_columns, sort,
+                             filterspec, pagesize, 0)
+        self.pagefoot()
+        index.db = index.cl = index.properties = None
+        index.clear()
 
     # XXX deviates from spec - loses the '+' (that's a reserved character
     # in URLS
     def list(self, sort=None, group=None, filter=None, columns=None,
-            filterspec=None, show_customization=None, show_nodes=1):
+            filterspec=None, show_customization=None, show_nodes=1, pagesize=None):
         ''' call the template index with the args
 
             :sort    - sort by prop name, optionally preceeded with '-'
@@ -435,7 +484,7 @@ function help_window(helpurl, width, height) {
         cl = self.db.classes[cn]
         self.pagehead(_('%(instancename)s: Index of %(classname)s')%{
             'classname': cn, 'instancename': self.instance.INSTANCE_NAME})
-        if sort is None: sort = self.index_arg(':sort')
+        if sort is None: sort = self.index_sort()
         if group is None: group = self.index_arg(':group')
         if filter is None: filter = self.index_arg(':filter')
         if columns is None: columns = self.index_arg(':columns')
@@ -446,12 +495,22 @@ function help_window(helpurl, width, height) {
             search_text = self.form['search_text'].value
         else:
             search_text = ''
+        if pagesize is None:
+            if self.form.has_key(':pagesize'):
+                pagesize = self.form[':pagesize'].value
+            else:
+                pagesize = '50'
+        pagesize = int(pagesize)
+        if self.form.has_key(':startwith'):
+            startwith = int(self.form[':startwith'].value)
+        else:
+            startwith = 0
 
         index = htmltemplate.IndexTemplate(self, self.instance.TEMPLATES, cn)
         try:
             index.render(filterspec, search_text, filter, columns, sort, 
                 group, show_customization=show_customization, 
-                show_nodes=show_nodes)
+                show_nodes=show_nodes, pagesize=pagesize, startwith=startwith)
         except htmltemplate.MissingTemplateError:
             self.basicClassEditPage()
         self.pagefoot()
@@ -550,13 +609,17 @@ function help_window(helpurl, width, height) {
         cn = self.form['classname'].value
         cl = self.db.classes[cn]
         props = self.form['properties'].value.split(',')
+        if cl.labelprop(1) in props:
+            sort = [cl.labelprop(1)]
+        else:
+            sort = props[0]
 
         w('<table border=1 cellspacing=0 cellpaddin=2>')
         w('<tr>')
         for name in props:
             w('<th align=left>%s</th>'%name)
         w('</tr>')
-        for nodeid in cl.list():
+        for nodeid in cl.filter(None, {}, sort, []): #cl.list():
             w('<tr>')
             for name in props:
                 value = cgi.escape(str(cl.get(nodeid, name)))
@@ -1314,6 +1377,7 @@ class ExtendedClient(Client):
     default_index_filter = ['status']
     default_index_columns = ['activity','status','title','assignedto']
     default_index_filterspec = {'status': ['1', '2', '3', '4', '5', '6', '7']}
+    default_pagesize = '50'
 
 def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')):
     '''Pull properties for the given class out of the form.
@@ -1395,6 +1459,9 @@ def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')):
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.132  2002/07/08 07:26:14  richard
+# ehem
+#
 # Revision 1.131  2002/07/08 06:53:57  richard
 # Not sure why the cgi_client had an indexer argument.
 #
index e245597fef39718571228c0c14f54736ce0b2ec0..2541ee348dc0bf0a8318f2b95e582859476765c4 100644 (file)
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: htmltemplate.py,v 1.94 2002-06-27 15:38:53 gmcm Exp $
+# $Id: htmltemplate.py,v 1.95 2002-07-08 15:32:06 gmcm Exp $
 
 __doc__ = """
 Template engine.
 """
 
 import os, re, StringIO, urllib, cgi, errno, types, urllib
+
 import hyperdb, date
 from i18n import _
 
@@ -725,7 +726,7 @@ class TemplateFunctions:
             return _('<input type="submit" name="submit" value="Submit New Entry">')
         else:
             return _('[Submit: not called from item]')
-
+        
     def do_classhelp(self, classname, properties, label='?', width='400',
             height='400'):
         '''pop up a javascript window with class help
@@ -741,6 +742,8 @@ class TemplateFunctions:
         return '<a href="javascript:help_window(\'classhelp?classname=%s&' \
             'properties=%s\', \'%s\', \'%s\')"><b>(%s)</b></a>'%(classname,
             properties, width, height, label)
+        #return '<a href="classhelp?classname=%s&properties=%s" target="classhelp"><b>(%s)</b></a>'%(classname,
+        #    properties, label)
 #
 #   INDEX TEMPLATES
 #
@@ -771,7 +774,7 @@ class IndexTemplateReplace:
                 return ''
         if m.group('display'):
             command = m.group('command')
-            return eval(command, self.globals, self.locals) 
+            return eval(command, self.globals, self.locals)
         return '*** unhandled match: %s'%str(m.groupdict())
 
 class IndexTemplate(TemplateFunctions):
@@ -789,23 +792,31 @@ class IndexTemplate(TemplateFunctions):
         self.cl = self.db.classes[self.classname]
         self.properties = self.cl.getprops()
 
+    def clear(self):
+        self.db = self.cl = self.properties = None
+        TemplateFunctions.clear(self)
+        
+    def buildurl(self, filterspec, search_text, filter, columns, sort, group, pagesize):
+        d = {'pagesize':pagesize, 'pagesize':pagesize, 'classname':self.classname}
+        d['filter'] = ','.join(map(urllib.quote,filter))
+        d['columns'] = ','.join(map(urllib.quote,columns))
+        d['sort'] = ','.join(map(urllib.quote,sort))
+        d['group'] = ','.join(map(urllib.quote,group))
+        tmp = []
+        for col, vals in filterspec.items():
+            vals = ','.join(map(urllib.quote,vals))
+            tmp.append('%s=%s' % (col, vals))
+        d['filters'] = '&'.join(tmp)
+        return '%(classname)s?%(filters)s&:sort=%(sort)s&:filter=%(filter)s&:group=%(group)s&:columns=%(columns)s&:pagesize=%(pagesize)s' % d
+    
     col_re=re.compile(r'<property\s+name="([^>]+)">')
     def render(self, filterspec={}, search_text='', filter=[], columns=[], 
             sort=[], group=[], show_display_form=1, nodeids=None,
-            show_customization=1, show_nodes=1):
+            show_customization=1, show_nodes=1, pagesize=50, startwith=0):
         
         self.filterspec = filterspec
 
         w = self.client.write
-        # get the filter template
-        try:
-            filter_template = open(os.path.join(self.templates,
-                self.classname+'.filter')).read()
-            all_filters = self.col_re.findall(filter_template)
-        except IOError, error:
-            if error.errno not in (errno.ENOENT, errno.ESRCH): raise
-            filter_template = None
-            all_filters = []
 
         # XXX deviate from spec here ...
         # load the index section template and figure the default columns from it
@@ -832,14 +843,8 @@ class IndexTemplate(TemplateFunctions):
         if (show_display_form and 
                 self.instance.FILTER_POSITION in ('top and bottom', 'top')):
             w('<form onSubmit="return submit_once()" action="%s">\n'%self.classname)
-            self.filter_section(filter_template, search_text, filter, 
-                columns, group, all_filters, all_columns, show_customization)
-            # make sure that the sorting doesn't get lost either
-            if sort:
-                w('<input type="hidden" name=":sort" value="%s">'%
-                    ','.join(sort))
-            w('</form>\n')
-
+            self.filter_section(search_text, filter, columns, group, all_columns, sort, filterspec,
+                                pagesize, startwith)
 
         # now display the index section
         w('<table width=100% border=0 cellspacing=0 cellpadding=2>\n')
@@ -847,7 +852,7 @@ class IndexTemplate(TemplateFunctions):
         for name in columns:
             cname = name.capitalize()
             if show_display_form:
-                sb = self.sortby(name, filterspec, columns, filter, group, sort)
+                sb = self.sortby(name, filterspec, columns, filter, group, sort, pagesize, startwith)
                 anchor = "%s?%s"%(self.classname, sb)
                 w('<td><span class="list-header"><a href="%s">%s</a></span></td>\n'%(
                     anchor, cname))
@@ -872,7 +877,7 @@ class IndexTemplate(TemplateFunctions):
                     matches = self.client.indexer.search(
                         search_text.split(' '), self.cl)
                 nodeids = self.cl.filter(matches, filterspec, sort, group)
-            for nodeid in nodeids:
+            for nodeid in nodeids[startwith:startwith+pagesize]:
                 # check for a group heading
                 if group_names:
                     this_group = [self.cl.get(nodeid, name, _('[no value]'))
@@ -884,13 +889,14 @@ class IndexTemplate(TemplateFunctions):
                             if isinstance(prop, hyperdb.Link):
                                 group_cl = self.db.classes[prop.classname]
                                 key = group_cl.getkey()
+                                if key is None:
+                                    key = group_cl.labelprop()
                                 value = self.cl.get(nodeid, name)
                                 if value is None:
                                     l.append(_('[unselected %(classname)s]')%{
                                         'classname': prop.classname})
                                 else:
-                                    l.append(group_cl.get(self.cl.get(nodeid,
-                                        name), key))
+                                    l.append(group_cl.get(value, key))
                             elif isinstance(prop, hyperdb.Multilink):
                                 group_cl = self.db.classes[prop.classname]
                                 key = group_cl.getkey()
@@ -919,20 +925,28 @@ class IndexTemplate(TemplateFunctions):
                 self.nodeid = None
 
         w('</table>')
+        # the previous and next links
+        if nodeids:
+            baseurl = self.buildurl(filterspec, search_text, filter, columns, sort, group, pagesize)
+            if startwith > 0:
+                prevurl = '<a href="%s&:startwith=%s">&lt;&lt; Previous page</a>' % \
+                          (baseurl, max(0, startwith-pagesize)) 
+            else:
+                prevurl = "" 
+            if startwith + pagesize < len(nodeids):
+                nexturl = '<a href="%s&:startwith=%s">Next page &gt;&gt;</a>' % (baseurl, startwith+pagesize)
+            else:
+                nexturl = ""
+            if prevurl or nexturl:
+                w('<table width="100%%"><tr><td width="50%%" align="center">%s</td><td width="50%%" align="center">%s</td></tr></table>' % (prevurl, nexturl))
 
         # display the filter section
         if (show_display_form and hasattr(self.instance, 'FILTER_POSITION') and
                 self.instance.FILTER_POSITION in ('top and bottom', 'bottom')):
             w('<form onSubmit="return submit_once()" action="%s">\n'%self.classname)
-            self.filter_section(filter_template, search_text, filter, 
-                columns, group, all_filters, all_columns, show_customization)
-            # make sure that the sorting doesn't get lost either
-            if sort:
-                w('<input type="hidden" name=":sort" value="%s">'%
-                    ','.join(sort))
-            w('</form>\n')
+            self.filter_section(search_text, filter, columns, group, all_columns, sort, filterspec,
+                                pagesize, startwith)
 
-        self.db = self.cl = self.properties = None
         self.clear()
 
     def node_matches(self, match, colspan):
@@ -965,121 +979,111 @@ class IndexTemplate(TemplateFunctions):
                     colspan, ', '.join(file_links)))
 
 
-    def filter_section(self, template, search_text, filter, columns, group, 
-            all_filters, all_columns, show_customization):
-        w = self.client.write
+    def filter_section(self, search_text, filter, columns, group, all_columns, sort, filterspec,
+                       pagesize, startwith):
 
-        # wrap the template in a single table to ensure the whole widget
-        # is displayed at once
-        w('<table><tr><td>')
-
-        if template and filter:
-            # display the filter section
-            w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
-            w('<tr class="location-bar">')
-            w(_(' <th align="left" colspan="2">Filter specification...</th>'))
-            w('</tr>')
-            w('<tr>')
-            w('<th class="location-bar">Search terms</th>')
-            w('<td><input name="search_text" value="%s" size="50"></td>'%(
-                search_text))
-            w('</tr>')
-            replace = IndexTemplateReplace(self.globals, locals(), filter)
-            w(replace.go(template))
-            w('<tr class="location-bar"><td width="1%%">&nbsp;</td>')
-            w(_('<td><input type="submit" name="action" value="Redisplay"></td></tr>'))
-            w('</table>')
-
-        # now add in the filter/columns/group/etc config table form
-        w('<input type="hidden" name="show_customization" value="%s">' %
-            show_customization )
-        w('<table width=100% border=0 cellspacing=0 cellpadding=2>\n')
-        names = []
-        seen = {}
-        for name in all_filters + all_columns:
-            if self.properties.has_key(name) and not seen.has_key(name):
-                names.append(name)
-            seen[name] = 1
-        if show_customization:
-            action = '-'
-        else:
-            action = '+'
-            # hide the values for filters, columns and grouping in the form
-            # if the customization widget is not visible
-            for name in names:
-                if all_filters and name in filter:
-                    w('<input type="hidden" name=":filter" value="%s">' % name)
-                if all_columns and name in columns:
-                    w('<input type="hidden" name=":columns" value="%s">' % name)
-                if all_columns and name in group:
-                    w('<input type="hidden" name=":group" value="%s">' % name)
-
-        # TODO: The widget style can go into the stylesheet
-        w(_('<th align="left" colspan=%s>'
-          '<input style="height : 1em; width : 1em; font-size: 12pt" type="submit" name="action" value="%s">&nbsp;View '
-          'customisation...</th></tr>\n')%(len(names)+1, action))
-
-        if not show_customization:
-            w('</table>\n')
-            return
-
-        w('<tr class="location-bar"><th>&nbsp;</th>')
-        for name in names:
-            w('<th>%s</th>'%name.capitalize())
-        w('</tr>\n')
+        sortspec = {}
+        for i in range(len(sort)):
+            mod = ''
+            colnm = sort[i]
+            if colnm[0] == '-':
+                mod = '-'
+                colnm = colnm[1:]
+            sortspec[colnm] = '%d%s' % (i+1, mod)
+            
+        startwith = 0
+        w = self.client.write
 
-        # Filter
-        if all_filters:
-            w(_('<tr><th width="1%" align=right class="location-bar">Filters</th>\n'))
-            for name in names:
-                if name not in all_filters:
-                    w('<td>&nbsp;</td>')
-                    continue
-                if name in filter: checked=' checked'
-                else: checked=''
-                w('<td align=middle>\n')
-                w(' <input type="checkbox" name=":filter" value="%s" '
-                  '%s></td>\n'%(name, checked))
-            w('</tr>\n')
-
-        # Columns
-        if all_columns:
-            w(_('<tr><th width="1%" align=right class="location-bar">Columns</th>\n'))
-            for name in names:
-                if name not in all_columns:
-                    w('<td>&nbsp;</td>')
-                    continue
-                if name in columns: checked=' checked'
-                else: checked=''
-                w('<td align=middle>\n')
-                w(' <input type="checkbox" name=":columns" value="%s"'
-                  '%s></td>\n'%(name, checked))
-            w('</tr>\n')
-
-            # Grouping
-            w(_('<tr><th width="1%" align=right class="location-bar">Grouping</th>\n'))
-            for name in names:
-                if name not in all_columns:
-                    w('<td>&nbsp;</td>')
-                    continue
-                if name in group: checked=' checked'
-                else: checked=''
-                w('<td align=middle>\n')
-                w(' <input type="checkbox" name=":group" value="%s"'
-                  '%s></td>\n'%(name, checked))
-            w('</tr>\n')
-
-        w('<tr class="location-bar"><td width="1%">&nbsp;</td>')
-        w('<td colspan="%s">'%len(names))
-        w(_('<input type="submit" name="action" value="Redisplay"></td>'))
-        w('</tr>\n')
+        # display the filter section
+        w(  '<br>\n')
+        w(  '<table border=0 cellspacing=0 cellpadding=0>\n')
+        w(  '<tr class="list-header">\n')
+        w(_(' <th align="left" colspan="7">Filter specification...</th>\n'))
+        w(  '</tr>\n')
+        # see if we have any indexed properties
+        if self.classname in self.db.config.HEADER_SEARCH_LINKS:
+        #if self.properties.has_key('messages') or self.properties.has_key('files'):
+            w(  '<tr class="location-bar">\n')
+            w(  ' <td align="right" class="form-label"><b>Search Terms</b></td>\n')
+            w(  ' <td>&nbsp</td>\n')
+            w(  ' <td colspan=5 class="form-text"><input type="text" name="search_text" value="%s" size="50"></td>\n' % search_text)
+            w(  '</tr>\n')
+        w(  '<tr class="location-bar">\n')
+        w(  ' <th align="center" width="20%">&nbsp;</th>\n')
+        w(_(' <th align="center" width="10%">Show</th>\n'))
+        w(_(' <th align="center" width="10%">Group</th>\n'))
+        w(_(' <th align="center" width="10%">Sort</th>\n'))
+        w(_(' <th colspan="3" align="center">Condition</th>\n'))
+        w(  '</tr>\n')
+        
+        for nm in all_columns:
+            propdescr = self.properties.get(nm, None)
+            if not propdescr:
+                print "hey sysadmin - %s is not a property of %r" % (nm, self.classname)
+                continue
+            w(  '<tr class="location-bar">\n')
+            w(_(' <td align="right" class="form-label"><b>%s</b></td>\n' % nm.capitalize()))
+            # show column - can't show multilinks
+            if isinstance(propdescr, hyperdb.Multilink):
+                w(' <td></td>\n')
+            else:
+                checked = columns and nm in columns or 0
+                checked = ('', 'checked')[checked]
+                w(' <td align="center" class="form-text"><input type="checkbox" name=":columns" value="%s" %s></td>\n' % (nm, checked) )
+            # can only group on Link 
+            if isinstance(propdescr, hyperdb.Link):
+                checked = group and nm in group or 0
+                checked = ('', 'checked')[checked]
+                w(' <td align="center" class="form-text"><input type="checkbox" name=":group" value="%s" %s></td>\n' % (nm, checked) )
+            else:
+                w(' <td></td>\n')
+            # sort - no sort on Multilinks
+            if isinstance(propdescr, hyperdb.Multilink):
+                w('<td></td>\n')
+            else:
+                val = sortspec.get(nm, '')
+                w('<td align="center" class="form-text"><input type="text" name=":%s_ss" size="3" value="%s"></td>\n' % (nm,val))
+            # condition
+            val = ''
+            if isinstance(propdescr, hyperdb.Link):
+                op = "is in&nbsp;"
+                xtra = '<a href="javascript:help_window(\'classhelp?classname=%s&properties=id,%s\', \'200\', \'400\')"><b>(list)</b></a>'\
+                       % (propdescr.classname, self.db.getclass(propdescr.classname).labelprop())
+                val = ','.join(filterspec.get(nm, ''))
+            elif isinstance(propdescr, hyperdb.Multilink):
+                op = "contains&nbsp;"
+                xtra = '<a href="javascript:help_window(\'classhelp?classname=%s&properties=id,%s\', \'200\', \'400\')"><b>(list)</b></a>'\
+                       % (propdescr.classname, self.db.getclass(propdescr.classname).labelprop())
+                val = ','.join(filterspec.get(nm, ''))
+            elif isinstance(propdescr, hyperdb.String) and nm != 'id':
+                op = "equals&nbsp;"
+                xtra = ""
+                val = filterspec.get(nm, '')
+            else:
+                w('<td></td><td></td><td></td></tr>\n')
+                continue
+            checked = filter and nm in filter or 0
+            checked = ('', 'checked')[checked]
+            w(  ' <td class="form-text"><input type="checkbox" name=":filter" value="%s" %s></td>\n' % (nm, checked))
+            w(_(' <td class="form-label" nowrap>%s</td><td class="form-text" nowrap><input type="text" name=":%s_fs" value="%s" size=50>%s</td>\n' % (op, nm, val, xtra)))
+            w(  '</tr>\n')
+        w('<tr class="location-bar">\n')
+       w(' <td colspan=7><hr></td>\n')
+       w('</tr>\n')
+       w('<tr class="location-bar">\n')
+       w(_(' <td align="right" class="form-label">Pagesize</td>\n'))
+       w(' <td colspan=2 align="center" class="form-text"><input type="text" name=":pagesize" size="3" value="%s"></td>\n' % pagesize)
+       w(' <td colspan=4></td>\n')
+       w('</tr>\n')
+       w('<tr class="location-bar">\n')
+       w(_(' <td align="right" class="form-label">Start With</td>\n'))
+       w(' <td colspan=2 align="center" class="form-text"><input type="text" name=":startwith" size="3" value="%s"></td>\n' % startwith)
+       w(' <td colspan=3 align="center" valign="center"><input type="submit" name="Query" value="Redisplay"></td>\n')
+       w(' <td></td>\n')
+       w('</tr>\n')
         w('</table>\n')
 
-        # and the outer table
-        w('</td></tr></table>')
-
-
-    def sortby(self, sort_name, filterspec, columns, filter, group, sort):
+    def sortby(self, sort_name, filterspec, columns, filter, group, sort, pagesize, startwith):
         l = []
         w = l.append
         for k, v in filterspec.items():
@@ -1094,6 +1098,8 @@ class IndexTemplate(TemplateFunctions):
             w(':filter=%s'%','.join(map(urllib.quote, filter)))
         if group:
             w(':group=%s'%','.join(map(urllib.quote, group)))
+        w(':pagesize=%s' % pagesize)
+        w(':startwith=%s' % startwith)
         m = []
         s_dir = ''
         for name in sort:
@@ -1115,8 +1121,7 @@ class IndexTemplate(TemplateFunctions):
         w(':sort=%s'%','.join(m[:2]))
         return '&'.join(l)
 
-
-#
+# 
 #   ITEM TEMPLATES
 #
 class ItemTemplateReplace:
@@ -1165,6 +1170,10 @@ class ItemTemplate(TemplateFunctions):
         self.cl = self.db.classes[self.classname]
         self.properties = self.cl.getprops()
 
+    def clear(self):
+        self.db = self.cl = self.properties = None
+        TemplateFunctions.clear(self)
+        
     def render(self, nodeid):
         self.nodeid = nodeid
 
@@ -1182,7 +1191,7 @@ class ItemTemplate(TemplateFunctions):
         replace = ItemTemplateReplace(self.globals, locals(), self.cl, nodeid)
         w(replace.go(s))
         w('</form>')
-        self.cl = self.db = self.properties = None
+        
         self.clear()
 
 
@@ -1201,6 +1210,10 @@ class NewItemTemplate(TemplateFunctions):
         self.cl = self.db.classes[self.classname]
         self.properties = self.cl.getprops()
 
+    def clear(self):
+        self.db = self.cl = None
+        TemplateFunctions.clear(self)
+        
     def render(self, form):
         self.form = form
         w = self.client.write
@@ -1219,11 +1232,19 @@ class NewItemTemplate(TemplateFunctions):
         replace = ItemTemplateReplace(self.globals, locals(), None, None)
         w(replace.go(s))
         w('</form>')
-        self.cl = self.db = None
+        
         self.clear()
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.94  2002/06/27 15:38:53  gmcm
+# Fix the cycles (a clear method, called after render, that removes
+# the bound methods from the globals dict).
+# Use cl.filter instead of cl.list followed by sortfunc. For some
+# backends (Metakit), filter can sort at C speeds, cutting >10 secs
+# off of filling in the <select...> box for assigned_to when you
+# have 600+ users.
+#
 # Revision 1.93  2002/06/27 12:05:25  gmcm
 # Default labelprops to id.
 # In history, make sure there's a .item before making a link / multilink into an href.