diff roundup/cgi/templating.py @ 1002:1798d2fa9fec

Hack hack... . Lots of cleanup in the classic html (stylesheet, search page, index page, ...) . Reinstated searching, but not query saving yet . Filtering only allows sorting and grouping by one property - all backends now implement this behaviour. . Nosy list journalling turned off by default, everything else is on. . Added some convenience methods (reverse, propchanged, [item] accesses, ...) . Did I mention the stylesheet is much cleaner now? :)
author Richard Jones <richard@users.sourceforge.net>
date Sun, 01 Sep 2002 04:32:30 +0000
parents 8d54c0f3198e
children f89b8d32291b
line wrap: on
line diff
--- a/roundup/cgi/templating.py	Sat Aug 31 22:09:26 2002 +0000
+++ b/roundup/cgi/templating.py	Sun Sep 01 04:32:30 2002 +0000
@@ -1,4 +1,4 @@
-import sys, cgi, urllib, os
+import sys, cgi, urllib, os, re
 
 from roundup import hyperdb, date
 from roundup.i18n import _
@@ -136,15 +136,15 @@
     def __repr__(self):
         return '<HTMLClass(0x%x) %s>'%(id(self), self.classname)
 
-    def __getattr__(self, attr):
+    def __getitem__(self, item):
         ''' return an HTMLItem instance'''
-        #print 'getattr', (self, attr)
-        if attr == 'creator':
+        #print 'getitem', (self, attr)
+        if item == 'creator':
             return HTMLUser(self.client)
 
-        if not self.props.has_key(attr):
-            raise AttributeError, attr
-        prop = self.props[attr]
+        if not self.props.has_key(item):
+            raise KeyError, item
+        prop = self.props[item]
 
         # look up the correct HTMLProperty class
         for klass, htmlklass in propclasses:
@@ -153,10 +153,17 @@
             else:
                 value = None
             if isinstance(prop, klass):
-                return htmlklass(self.db, '', prop, attr, value)
+                return htmlklass(self.db, '', prop, item, value)
 
         # no good
-        raise AttributeError, attr
+        raise KeyError, item
+
+    def __getattr__(self, attr):
+        ''' convenience access '''
+        try:
+            return self[attr]
+        except KeyError:
+            raise AttributeError, attr
 
     def properties(self):
         ''' Return HTMLProperty for all props
@@ -248,29 +255,33 @@
     def __repr__(self):
         return '<HTMLItem(0x%x) %s %s>'%(id(self), self.classname, self.nodeid)
 
-    def __getattr__(self, attr):
+    def __getitem__(self, item):
         ''' return an HTMLItem instance'''
-        #print 'getattr', (self, attr)
-        if attr == 'id':
+        if item == 'id':
             return self.nodeid
-
-        if not self.props.has_key(attr):
-            raise AttributeError, attr
-        prop = self.props[attr]
+        if not self.props.has_key(item):
+            raise KeyError, item
+        prop = self.props[item]
 
         # get the value, handling missing values
-        value = self.klass.get(self.nodeid, attr, None)
+        value = self.klass.get(self.nodeid, item, None)
         if value is None:
-            if isinstance(self.props[attr], hyperdb.Multilink):
+            if isinstance(self.props[item], hyperdb.Multilink):
                 value = []
 
         # look up the correct HTMLProperty class
         for klass, htmlklass in propclasses:
             if isinstance(prop, klass):
-                return htmlklass(self.db, self.nodeid, prop, attr, value)
+                return htmlklass(self.db, self.nodeid, prop, item, value)
+
+        raise KeyErorr, item
 
-        # no good
-        raise AttributeError, attr
+    def __getattr__(self, attr):
+        ''' convenience access to properties '''
+        try:
+            return self[attr]
+        except KeyError:
+            raise AttributeError, attr
     
     def submit(self, label="Submit Changes"):
         ''' Generate a submit button (and action hidden element)
@@ -613,7 +624,7 @@
     def plain(self, escape=0):
         if self.value is None:
             return _('[unselected]')
-        linkcl = self.db.classes[self.klass.classname]
+        linkcl = self.db.classes[self.prop.classname]
         k = linkcl.labelprop(1)
         value = str(linkcl.get(self.value, k))
         if escape:
@@ -688,10 +699,10 @@
             s = 'selected '
         l.append(_('<option %svalue="-1">- no selection -</option>')%s)
         if linkcl.getprops().has_key('order'):  
-            sort_on = 'order'  
+            sort_on = ('+', 'order')
         else:  
-            sort_on = linkcl.labelprop() 
-        options = linkcl.filter(None, conditions, [sort_on], []) 
+            sort_on = ('+', linkcl.labelprop())
+        options = linkcl.filter(None, conditions, sort_on, (None, None))
         for optionid in options:
             option = linkcl.get(optionid, k)
             s = ''
@@ -735,6 +746,12 @@
         value = self.value[num]
         return HTMLItem(self.db, self.prop.classname, value)
 
+    def reverse(self):
+        ''' return the list in reverse order '''
+        l = self.value[:]
+        l.reverse()
+        return [HTMLItem(self.db, self.prop.classname, value) for value in l]
+
     def plain(self, escape=0):
         linkcl = self.db.classes[self.prop.classname]
         k = linkcl.labelprop(1)
@@ -772,10 +789,10 @@
 
         linkcl = self.db.getclass(self.prop.classname)
         if linkcl.getprops().has_key('order'):  
-            sort_on = 'order'  
+            sort_on = ('+', 'order')
         else:  
-            sort_on = linkcl.labelprop()
-        options = linkcl.filter(None, conditions, [sort_on], []) 
+            sort_on = ('+', linkcl.labelprop())
+        options = linkcl.filter(None, conditions, sort_on, (None,None)) 
         height = height or min(len(options), 7)
         l = ['<select multiple name="%s" size="%s">'%(self.name, height)]
         k = linkcl.labelprop(1)
@@ -858,19 +875,48 @@
         if self.form.has_key(':columns'):
             for entry in handleListCGIValue(self.form[':columns']):
                 self.columns[entry] = 1
-        self.sort = []
+
+        # sorting
+        self.sort = (None, None)
         if self.form.has_key(':sort'):
-            self.sort = handleListCGIValue(self.form[':sort'])
-        self.group = []
+            sort = self.form[':sort'].value
+            if sort.startswith('-'):
+                self.sort = ('-', sort[1:])
+            else:
+                self.sort = ('+', sort)
+        if self.form.has_key(':sortdir'):
+            self.sort = ('-', self.sort[1])
+
+        # grouping
+        self.group = (None, None)
         if self.form.has_key(':group'):
-            self.group = handleListCGIValue(self.form[':group'])
+            group = self.form[':group'].value
+            if group.startswith('-'):
+                self.group = ('-', group[1:])
+            else:
+                self.group = ('+', group)
+        if self.form.has_key(':groupdir'):
+            self.group = ('-', self.group[1])
+
+        # filtering
         self.filter = []
         if self.form.has_key(':filter'):
             self.filter = handleListCGIValue(self.form[':filter'])
         self.filterspec = {}
+        props = self.client.db.getclass(self.classname).getprops()
         for name in self.filter:
             if self.form.has_key(name):
-                self.filterspec[name]=handleListCGIValue(self.form[name])
+                prop = props[name]
+                if (isinstance(prop, hyperdb.Link) or
+                        isinstance(prop, hyperdb.Multilink)):
+                    self.filterspec[name] = handleListCGIValue(self.form[name])
+                else:
+                    self.filterspec[name] = self.form[name].value
+
+        # full-text search argument
+        self.search_text = None
+        if self.form.has_key(':search_text'):
+            self.search_text = self.form[':search_text'].value
 
     def __str__(self):
         d = {}
@@ -896,30 +942,32 @@
 env: %(env)s
 '''%d
 
-    def indexargs_form(self):
+    def indexargs_form(self, columns=1, sort=1, group=1, filter=1,
+            filterspec=1):
         ''' return the current index args as form elements '''
         l = []
         s = '<input type="hidden" name="%s" value="%s">'
-        if self.columns:
+        if columns and self.columns:
             l.append(s%(':columns', ','.join(self.columns.keys())))
-        if self.sort:
-            l.append(s%(':sort', ','.join(self.sort)))
-        if self.group:
-            l.append(s%(':group', ','.join(self.group)))
-        if self.filter:
+        if sort and self.sort is not None:
+            l.append(s%(':sort', self.sort))
+        if group and self.group is not None:
+            l.append(s%(':group', self.group))
+        if filter and self.filter:
             l.append(s%(':filter', ','.join(self.filter)))
-        for k,v in self.filterspec.items():
-            l.append(s%(k, ','.join(v)))
+        if filterspec:
+            for k,v in self.filterspec.items():
+                l.append(s%(k, ','.join(v)))
         return '\n'.join(l)
 
     def indexargs_href(self, url, args):
         l = ['%s=%s'%(k,v) for k,v in args.items()]
         if self.columns:
             l.append(':columns=%s'%(','.join(self.columns.keys())))
-        if self.sort:
-            l.append(':sort=%s'%(','.join(self.sort)))
-        if self.group:
-            l.append(':group=%s'%(','.join(self.group)))
+        if self.sort is not None:
+            l.append(':sort=%s'%self.sort)
+        if self.group is not None:
+            l.append(':group=%s'%self.group)
         if self.filter:
             l.append(':filter=%s'%(','.join(self.filter)))
         for k,v in self.filterspec.items():
@@ -954,7 +1002,12 @@
 
         # get the list of ids we're batching over
         klass = self.client.db.getclass(self.classname)
-        l = klass.filter(None, filterspec, sort, group)
+        if self.search_text:
+            matches = self.client.db.indexer.search(
+                re.findall(r'\b\w{2,25}\b', self.search_text), klass)
+        else:
+            matches = None
+        l = klass.filter(matches, filterspec, sort, group)
 
         # figure batch args
         if self.form.has_key(':pagesize'):
@@ -973,6 +1026,8 @@
     def __init__(self, client, classname, l, size, start, end=0, orphan=0, overlap=0):
         self.client = client
         self.classname = classname
+        self.last_index = self.last_item = None
+        self.current_item = None
         ZTUtils.Batch.__init__(self, l, size, start, end, orphan, overlap)
 
     # overwrite so we can late-instantiate the HTMLItem instance
@@ -983,13 +1038,28 @@
         
         if index >= self.length: raise IndexError, index
 
+        # move the last_item along - but only if the fetched index changes
+        # (for some reason, index 0 is fetched twice)
+        if index != self.last_index:
+            self.last_item = self.current_item
+            self.last_index = index
+
         # wrap the return in an HTMLItem
-        return HTMLItem(self.client.db, self.classname,
+        self.current_item = HTMLItem(self.client.db, self.classname,
             self._sequence[index+self.first])
+        return self.current_item
+
+    def propchanged(self, property):
+        ''' Detect if the property marked as being the group property
+            changed in the last iteration fetch
+        '''
+        if (self.last_item is None or
+                self.last_item[property] != self.current_item[property]):
+            return 1
+        return 0
 
     # override these 'cos we don't have access to acquisition
     def previous(self):
-        print self.start
         if self.start == 1:
             return None
         return Batch(self.client, self.classname, self._sequence, self._size,

Roundup Issue Tracker: http://roundup-tracker.org/