Mercurial > p > roundup > code
diff roundup/cgi/templating.py @ 985:55ab0c5b49f9
New CGI interface support
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Fri, 30 Aug 2002 08:28:44 +0000 |
| parents | |
| children | 8d54c0f3198e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/roundup/cgi/templating.py Fri Aug 30 08:28:44 2002 +0000 @@ -0,0 +1,998 @@ +import sys, cgi, urllib + +from roundup import hyperdb, date +from roundup.i18n import _ + + +try: + import StructuredText +except ImportError: + StructuredText = None + +# Make sure these modules are loaded +# I need these to run PageTemplates outside of Zope :( +# If we're running in a Zope environment, these modules will be loaded +# already... +if not sys.modules.has_key('zLOG'): + import zLOG + sys.modules['zLOG'] = zLOG +if not sys.modules.has_key('MultiMapping'): + import MultiMapping + sys.modules['MultiMapping'] = MultiMapping +if not sys.modules.has_key('ComputedAttribute'): + import ComputedAttribute + sys.modules['ComputedAttribute'] = ComputedAttribute +if not sys.modules.has_key('ExtensionClass'): + import ExtensionClass + sys.modules['ExtensionClass'] = ExtensionClass +if not sys.modules.has_key('Acquisition'): + import Acquisition + sys.modules['Acquisition'] = Acquisition + +# now it's safe to import PageTemplates and ZTUtils +from PageTemplates import PageTemplate +import ZTUtils + +class RoundupPageTemplate(PageTemplate.PageTemplate): + ''' A Roundup-specific PageTemplate. + + Interrogate the client to set up the various template variables to + be available: + + *class* + The current class of node being displayed as an HTMLClass + instance. + *item* + The current node from the database, if we're viewing a specific + node, as an HTMLItem instance. If it doesn't exist, then we're + on a new item page. + (*classname*) + this is one of two things: + + 1. the *item* is also available under its classname, so a *user* + node would also be available under the name *user*. This is + also an HTMLItem instance. + 2. if there's no *item* then the current class is available + through this name, thus "user/name" and "user/name/menu" will + still work - the latter will pull information from the form + if it can. + *form* + The current CGI form information as a mapping of form argument + name to value + *request* + Includes information about the current request, including: + - the url + - the current index information (``filterspec``, ``filter`` args, + ``properties``, etc) parsed out of the form. + - methods for easy filterspec link generation + - *user*, the current user node as an HTMLItem instance + *instance* + The current instance + *db* + The current database, through which db.config may be reached. + + Maybe also: + + *modules* + python modules made available (XXX: not sure what's actually in + there tho) + ''' + def __init__(self, client, classname=None, request=None): + ''' Extract the vars from the client and install in the context. + ''' + self.client = client + self.classname = classname or self.client.classname + self.request = request or HTMLRequest(self.client) + + def pt_getContext(self): + c = { + 'klass': HTMLClass(self.client, self.classname), + 'options': {}, + 'nothing': None, + 'request': self.request, + 'content': self.client.content, + 'db': HTMLDatabase(self.client), + 'instance': self.client.instance + } + # add in the item if there is one + if self.client.nodeid: + c['item'] = HTMLItem(self.client.db, self.classname, + self.client.nodeid) + c[self.classname] = c['item'] + else: + c[self.classname] = c['klass'] + return c + + def render(self, *args, **kwargs): + if not kwargs.has_key('args'): + kwargs['args'] = args + return self.pt_render(extra_context={'options': kwargs}) + +class HTMLDatabase: + ''' Return HTMLClasses for valid class fetches + ''' + def __init__(self, client): + self.client = client + self.config = client.db.config + def __getattr__(self, attr): + self.client.db.getclass(attr) + return HTMLClass(self.client, attr) + def classes(self): + l = self.client.db.classes.keys() + l.sort() + return [HTMLClass(self.client, cn) for cn in l] + +class HTMLClass: + ''' Accesses through a class (either through *class* or *db.<classname>*) + ''' + def __init__(self, client, classname): + self.client = client + self.db = client.db + self.classname = classname + if classname is not None: + self.klass = self.db.getclass(self.classname) + self.props = self.klass.getprops() + + def __repr__(self): + return '<HTMLClass(0x%x) %s>'%(id(self), self.classname) + + def __getattr__(self, attr): + ''' return an HTMLItem instance''' + #print 'getattr', (self, attr) + if attr == 'creator': + return HTMLUser(self.client) + + if not self.props.has_key(attr): + raise AttributeError, attr + prop = self.props[attr] + + # look up the correct HTMLProperty class + for klass, htmlklass in propclasses: + if isinstance(prop, hyperdb.Multilink): + value = [] + else: + value = None + if isinstance(prop, klass): + return htmlklass(self.db, '', prop, attr, value) + + # no good + raise AttributeError, attr + + def properties(self): + ''' Return HTMLProperty for all props + ''' + l = [] + for name, prop in self.props.items(): + for klass, htmlklass in propclasses: + if isinstance(prop, hyperdb.Multilink): + value = [] + else: + value = None + if isinstance(prop, klass): + l.append(htmlklass(self.db, '', prop, name, value)) + return l + + def list(self): + l = [HTMLItem(self.db, self.classname, x) for x in self.klass.list()] + return l + + def filter(self, request=None): + ''' Return a list of items from this class, filtered and sorted + by the current requested filterspec/filter/sort/group args + ''' + if request is not None: + filterspec = request.filterspec + sort = request.sort + group = request.group + l = [HTMLItem(self.db, self.classname, x) + for x in self.klass.filter(None, filterspec, sort, group)] + return l + + def classhelp(self, properties, label='?', width='400', height='400'): + '''pop up a javascript window with class help + + This generates a link to a popup window which displays the + properties indicated by "properties" of the class named by + "classname". The "properties" should be a comma-separated list + (eg. 'id,name,description'). + + You may optionally override the label displayed, the width and + height. The popup window will be resizable and scrollable. + ''' + return '<a href="javascript:help_window(\'classhelp?classname=%s&' \ + 'properties=%s\', \'%s\', \'%s\')"><b>(%s)</b></a>'%(self.classname, + properties, width, height, label) + + def history(self): + return 'New node - no history' + + def renderWith(self, name, **kwargs): + ''' Render this class with the given template. + ''' + # create a new request and override the specified args + req = HTMLRequest(self.client) + req.classname = self.classname + req.__dict__.update(kwargs) + + # new template, using the specified classname and request + pt = RoundupPageTemplate(self.client, self.classname, req) + + # use the specified template + name = self.classname + '.' + name + pt.write(open('/tmp/test/html/%s'%name).read()) + pt.id = name + + # XXX handle PT rendering errors here nicely + try: + return pt.render() + except PageTemplate.PTRuntimeError, message: + return '<strong>%s</strong><ol>%s</ol>'%(message, + cgi.escape('<li>'.join(pt._v_errors))) + +class HTMLItem: + ''' Accesses through an *item* + ''' + def __init__(self, db, classname, nodeid): + self.db = db + self.classname = classname + self.nodeid = nodeid + self.klass = self.db.getclass(classname) + self.props = self.klass.getprops() + + def __repr__(self): + return '<HTMLItem(0x%x) %s %s>'%(id(self), self.classname, self.nodeid) + + def __getattr__(self, attr): + ''' return an HTMLItem instance''' + #print 'getattr', (self, attr) + if attr == 'id': + return self.nodeid + + if not self.props.has_key(attr): + raise AttributeError, attr + prop = self.props[attr] + + # get the value, handling missing values + value = self.klass.get(self.nodeid, attr, None) + if value is None: + if isinstance(self.props[attr], 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) + + # no good + raise AttributeError, attr + + # XXX this probably should just return the history items, not the HTML + def history(self, direction='descending'): + l = ['<table width=100% border=0 cellspacing=0 cellpadding=2>', + '<tr class="list-header">', + _('<th align=left><span class="list-item">Date</span></th>'), + _('<th align=left><span class="list-item">User</span></th>'), + _('<th align=left><span class="list-item">Action</span></th>'), + _('<th align=left><span class="list-item">Args</span></th>'), + '</tr>'] + comments = {} + history = self.klass.history(self.nodeid) + history.sort() + if direction == 'descending': + history.reverse() + for id, evt_date, user, action, args in history: + date_s = str(evt_date).replace("."," ") + arg_s = '' + if action == 'link' and type(args) == type(()): + if len(args) == 3: + linkcl, linkid, key = args + arg_s += '<a href="%s%s">%s%s %s</a>'%(linkcl, linkid, + linkcl, linkid, key) + else: + arg_s = str(args) + + elif action == 'unlink' and type(args) == type(()): + if len(args) == 3: + linkcl, linkid, key = args + arg_s += '<a href="%s%s">%s%s %s</a>'%(linkcl, linkid, + linkcl, linkid, key) + else: + arg_s = str(args) + + elif type(args) == type({}): + cell = [] + for k in args.keys(): + # try to get the relevant property and treat it + # specially + try: + prop = props[k] + except: + prop = None + if prop is not None: + if args[k] and (isinstance(prop, hyperdb.Multilink) or + isinstance(prop, hyperdb.Link)): + # figure what the link class is + classname = prop.classname + try: + linkcl = self.db.getclass(classname) + except KeyError: + labelprop = None + comments[classname] = _('''The linked class + %(classname)s no longer exists''')%locals() + labelprop = linkcl.labelprop(1) +# hrefable = os.path.exists( +# os.path.join(self.instance.TEMPLATES, +# classname+'.item')) + + if isinstance(prop, hyperdb.Multilink) and \ + len(args[k]) > 0: + ml = [] + for linkid in args[k]: + if isinstance(linkid, type(())): + sublabel = linkid[0] + ' ' + linkids = linkid[1] + else: + sublabel = '' + linkids = [linkid] + subml = [] + for linkid in linkids: + label = classname + linkid + # if we have a label property, try to use it + # TODO: test for node existence even when + # there's no labelprop! + try: + if labelprop is not None: + label = linkcl.get(linkid, labelprop) + except IndexError: + comments['no_link'] = _('''<strike>The + linked node no longer + exists</strike>''') + subml.append('<strike>%s</strike>'%label) + else: +# if hrefable: + subml.append('<a href="%s%s">%s</a>'%( + classname, linkid, label)) + ml.append(sublabel + ', '.join(subml)) + cell.append('%s:\n %s'%(k, ', '.join(ml))) + elif isinstance(prop, hyperdb.Link) and args[k]: + label = classname + args[k] + # if we have a label property, try to use it + # TODO: test for node existence even when + # there's no labelprop! + if labelprop is not None: + try: + label = linkcl.get(args[k], labelprop) + except IndexError: + comments['no_link'] = _('''<strike>The + linked node no longer + exists</strike>''') + cell.append(' <strike>%s</strike>,\n'%label) + # "flag" this is done .... euwww + label = None + if label is not None: + if hrefable: + cell.append('%s: <a href="%s%s">%s</a>\n'%(k, + classname, args[k], label)) + else: + cell.append('%s: %s' % (k,label)) + + elif isinstance(prop, hyperdb.Date) and args[k]: + d = date.Date(args[k]) + cell.append('%s: %s'%(k, str(d))) + + elif isinstance(prop, hyperdb.Interval) and args[k]: + d = date.Interval(args[k]) + cell.append('%s: %s'%(k, str(d))) + + elif isinstance(prop, hyperdb.String) and args[k]: + cell.append('%s: %s'%(k, cgi.escape(args[k]))) + + elif not args[k]: + cell.append('%s: (no value)\n'%k) + + else: + cell.append('%s: %s\n'%(k, str(args[k]))) + else: + # property no longer exists + comments['no_exist'] = _('''<em>The indicated property + no longer exists</em>''') + cell.append('<em>%s: %s</em>\n'%(k, str(args[k]))) + arg_s = '<br />'.join(cell) + else: + # unkown event!! + comments['unknown'] = _('''<strong><em>This event is not + handled by the history display!</em></strong>''') + arg_s = '<strong><em>' + str(args) + '</em></strong>' + date_s = date_s.replace(' ', ' ') + l.append('<tr><td nowrap valign=top>%s</td><td valign=top>%s</td>' + '<td valign=top>%s</td><td valign=top>%s</td></tr>'%(date_s, + user, action, arg_s)) + if comments: + l.append(_('<tr><td colspan=4><strong>Note:</strong></td></tr>')) + for entry in comments.values(): + l.append('<tr><td colspan=4>%s</td></tr>'%entry) + l.append('</table>') + return '\n'.join(l) + + def remove(self): + # XXX do what? + return '' + +class HTMLUser(HTMLItem): + ''' Accesses through the *user* (a special case of item) + ''' + def __init__(self, client): + HTMLItem.__init__(self, client.db, 'user', client.userid) + self.default_classname = client.classname + self.userid = client.userid + + # used for security checks + self.security = client.db.security + _marker = [] + def hasPermission(self, role, classname=_marker): + ''' Determine if the user has the Role. + + The class being tested defaults to the template's class, but may + be overidden for this test by suppling an alternate classname. + ''' + if classname is self._marker: + classname = self.default_classname + return self.security.hasPermission(role, self.userid, classname) + +class HTMLProperty: + ''' String, Number, Date, Interval HTMLProperty + + A wrapper object which may be stringified for the plain() behaviour. + ''' + def __init__(self, db, nodeid, prop, name, value): + self.db = db + self.nodeid = nodeid + self.prop = prop + self.name = name + self.value = value + def __repr__(self): + return '<HTMLProperty(0x%x) %s %r %r>'%(id(self), self.name, self.prop, self.value) + def __str__(self): + return self.plain() + def __cmp__(self, other): + if isinstance(other, HTMLProperty): + return cmp(self.value, other.value) + return cmp(self.value, other) + +class StringHTMLProperty(HTMLProperty): + def plain(self, escape=0): + if self.value is None: + return '' + if escape: + return cgi.escape(str(self.value)) + return str(self.value) + + def stext(self, escape=0): + s = self.plain(escape=escape) + if not StructuredText: + return s + return StructuredText(s,level=1,header=0) + + def field(self, size = 30): + if self.value is None: + value = '' + else: + value = cgi.escape(str(self.value)) + value = '"'.join(value.split('"')) + return '<input name="%s" value="%s" size="%s">'%(self.name, value, size) + + def multiline(self, escape=0, rows=5, cols=40): + if self.value is None: + value = '' + else: + value = cgi.escape(str(self.value)) + value = '"'.join(value.split('"')) + return '<textarea name="%s" rows="%s" cols="%s">%s</textarea>'%( + self.name, rows, cols, value) + + def email(self, escape=1): + ''' fudge email ''' + if self.value is None: value = '' + else: value = str(self.value) + value = value.replace('@', ' at ') + value = value.replace('.', ' ') + if escape: + value = cgi.escape(value) + return value + +class PasswordHTMLProperty(HTMLProperty): + def plain(self): + if self.value is None: + return '' + return _('*encrypted*') + + def field(self, size = 30): + return '<input type="password" name="%s" size="%s">'%(self.name, size) + +class NumberHTMLProperty(HTMLProperty): + def plain(self): + return str(self.value) + + def field(self, size = 30): + if self.value is None: + value = '' + else: + value = cgi.escape(str(self.value)) + value = '"'.join(value.split('"')) + return '<input name="%s" value="%s" size="%s">'%(self.name, value, size) + +class BooleanHTMLProperty(HTMLProperty): + def plain(self): + if self.value is None: + return '' + return self.value and "Yes" or "No" + + def field(self): + checked = self.value and "checked" or "" + s = '<input type="radio" name="%s" value="yes" %s>Yes'%(self.name, + checked) + if checked: + checked = "" + else: + checked = "checked" + s += '<input type="radio" name="%s" value="no" %s>No'%(self.name, + checked) + return s + +class DateHTMLProperty(HTMLProperty): + def plain(self): + if self.value is None: + return '' + return str(self.value) + + def field(self, size = 30): + if self.value is None: + value = '' + else: + value = cgi.escape(str(self.value)) + value = '"'.join(value.split('"')) + return '<input name="%s" value="%s" size="%s">'%(self.name, value, size) + + def reldate(self, pretty=1): + if not self.value: + return '' + + # figure the interval + interval = date.Date('.') - self.value + if pretty: + return interval.pretty() + return str(interval) + +class IntervalHTMLProperty(HTMLProperty): + def plain(self): + if self.value is None: + return '' + return str(self.value) + + def pretty(self): + return self.value.pretty() + + def field(self, size = 30): + if self.value is None: + value = '' + else: + value = cgi.escape(str(self.value)) + value = '"'.join(value.split('"')) + return '<input name="%s" value="%s" size="%s">'%(self.name, value, size) + +class LinkHTMLProperty(HTMLProperty): + ''' Link HTMLProperty + Include the above as well as being able to access the class + information. Stringifying the object itself results in the value + from the item being displayed. Accessing attributes of this object + result in the appropriate entry from the class being queried for the + property accessed (so item/assignedto/name would look up the user + entry identified by the assignedto property on item, and then the + name property of that user) + ''' + def __getattr__(self, attr): + ''' return a new HTMLItem ''' + #print 'getattr', (self, attr, self.value) + if not self.value: + raise AttributeError, "Can't access missing value" + i = HTMLItem(self.db, self.prop.classname, self.value) + return getattr(i, attr) + + def plain(self, escape=0): + if self.value is None: + return _('[unselected]') + linkcl = self.db.classes[self.klass.classname] + k = linkcl.labelprop(1) + value = str(linkcl.get(self.value, k)) + if escape: + value = cgi.escape(value) + return value + + # XXX most of the stuff from here down is of dubious utility - it's easy + # enough to do in the template by hand (and in some cases, it's shorter + # and clearer... + + def field(self): + linkcl = self.db.getclass(self.prop.classname) + if linkcl.getprops().has_key('order'): + sort_on = 'order' + else: + sort_on = linkcl.labelprop() + options = linkcl.filter(None, {}, [sort_on], []) + # TODO: make this a field display, not a menu one! + l = ['<select name="%s">'%property] + k = linkcl.labelprop(1) + if value is None: + s = 'selected ' + else: + s = '' + l.append(_('<option %svalue="-1">- no selection -</option>')%s) + for optionid in options: + option = linkcl.get(optionid, k) + s = '' + if optionid == value: + s = 'selected ' + if showid: + lab = '%s%s: %s'%(self.prop.classname, optionid, option) + else: + lab = option + if size is not None and len(lab) > size: + lab = lab[:size-3] + '...' + lab = cgi.escape(lab) + l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab)) + l.append('</select>') + return '\n'.join(l) + + def download(self, showid=0): + linkname = self.prop.classname + linkcl = self.db.getclass(linkname) + k = linkcl.labelprop(1) + linkvalue = cgi.escape(str(linkcl.get(self.value, k))) + if showid: + label = value + title = ' title="%s"'%linkvalue + # note ... this should be urllib.quote(linkcl.get(value, k)) + else: + label = linkvalue + title = '' + return '<a href="%s%s/%s"%s>%s</a>'%(linkname, self.value, + linkvalue, title, label) + + def menu(self, size=None, height=None, showid=0, additional=[], + **conditions): + value = self.value + + # sort function + sortfunc = make_sort_function(self.db, self.prop.classname) + + # force the value to be a single choice + if isinstance(value, type('')): + value = value[0] + linkcl = self.db.getclass(self.prop.classname) + l = ['<select name="%s">'%self.name] + k = linkcl.labelprop(1) + s = '' + if value is None: + s = 'selected ' + l.append(_('<option %svalue="-1">- no selection -</option>')%s) + if linkcl.getprops().has_key('order'): + sort_on = 'order' + else: + sort_on = linkcl.labelprop() + options = linkcl.filter(None, conditions, [sort_on], []) + for optionid in options: + option = linkcl.get(optionid, k) + s = '' + if value in [optionid, option]: + s = 'selected ' + if showid: + lab = '%s%s: %s'%(self.prop.classname, optionid, option) + else: + lab = option + if size is not None and len(lab) > size: + lab = lab[:size-3] + '...' + if additional: + m = [] + for propname in additional: + m.append(linkcl.get(optionid, propname)) + lab = lab + ' (%s)'%', '.join(map(str, m)) + lab = cgi.escape(lab) + l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab)) + l.append('</select>') + return '\n'.join(l) + +# def checklist(self, ...) + +class MultilinkHTMLProperty(HTMLProperty): + ''' Multilink HTMLProperty + + Also be iterable, returning a wrapper object like the Link case for + each entry in the multilink. + ''' + def __len__(self): + ''' length of the multilink ''' + return len(self.value) + + def __getattr__(self, attr): + ''' no extended attribute accesses make sense here ''' + raise AttributeError, attr + + def __getitem__(self, num): + ''' iterate and return a new HTMLItem ''' + #print 'getitem', (self, num) + value = self.value[num] + return HTMLItem(self.db, self.prop.classname, value) + + def plain(self, escape=0): + linkcl = self.db.classes[self.prop.classname] + k = linkcl.labelprop(1) + labels = [] + for v in self.value: + labels.append(linkcl.get(v, k)) + value = ', '.join(labels) + if escape: + value = cgi.escape(value) + return value + + # XXX most of the stuff from here down is of dubious utility - it's easy + # enough to do in the template by hand (and in some cases, it's shorter + # and clearer... + + def field(self, size=30, showid=0): + sortfunc = make_sort_function(self.db, self.prop.classname) + linkcl = self.db.getclass(self.prop.classname) + value = self.value[:] + if value: + value.sort(sortfunc) + # map the id to the label property + if not showid: + k = linkcl.labelprop(1) + value = [linkcl.get(v, k) for v in value] + value = cgi.escape(','.join(value)) + return '<input name="%s" size="%s" value="%s">'%(self.name, size, value) + + def menu(self, size=None, height=None, showid=0, additional=[], + **conditions): + value = self.value + + # sort function + sortfunc = make_sort_function(self.db, self.prop.classname) + + linkcl = self.db.getclass(self.prop.classname) + if linkcl.getprops().has_key('order'): + sort_on = 'order' + else: + sort_on = linkcl.labelprop() + options = linkcl.filter(None, conditions, [sort_on], []) + height = height or min(len(options), 7) + l = ['<select multiple name="%s" size="%s">'%(self.name, height)] + k = linkcl.labelprop(1) + for optionid in options: + option = linkcl.get(optionid, k) + s = '' + if optionid in value or option in value: + s = 'selected ' + if showid: + lab = '%s%s: %s'%(self.prop.classname, optionid, option) + else: + lab = option + if size is not None and len(lab) > size: + lab = lab[:size-3] + '...' + if additional: + m = [] + for propname in additional: + m.append(linkcl.get(optionid, propname)) + lab = lab + ' (%s)'%', '.join(m) + lab = cgi.escape(lab) + l.append('<option %svalue="%s">%s</option>'%(s, optionid, + lab)) + l.append('</select>') + return '\n'.join(l) + +# set the propclasses for HTMLItem +propclasses = ( + (hyperdb.String, StringHTMLProperty), + (hyperdb.Number, NumberHTMLProperty), + (hyperdb.Boolean, BooleanHTMLProperty), + (hyperdb.Date, DateHTMLProperty), + (hyperdb.Interval, IntervalHTMLProperty), + (hyperdb.Password, PasswordHTMLProperty), + (hyperdb.Link, LinkHTMLProperty), + (hyperdb.Multilink, MultilinkHTMLProperty), +) + +def make_sort_function(db, classname): + '''Make a sort function for a given class + ''' + linkcl = db.getclass(classname) + if linkcl.getprops().has_key('order'): + sort_on = 'order' + else: + sort_on = linkcl.labelprop() + def sortfunc(a, b, linkcl=linkcl, sort_on=sort_on): + return cmp(linkcl.get(a, sort_on), linkcl.get(b, sort_on)) + return sortfunc + +def handleListCGIValue(value): + ''' Value is either a single item or a list of items. Each item has a + .value that we're actually interested in. + ''' + if isinstance(value, type([])): + return [value.value for value in value] + else: + return value.value.split(',') + +# XXX This is starting to look a lot (in data terms) like the client object +# itself! +class HTMLRequest: + ''' The *request*, holding the CGI form and environment. + + ''' + def __init__(self, client): + self.client = client + + # easier access vars + self.form = client.form + self.env = client.env + self.base = client.base + self.user = HTMLUser(client) + + # store the current class name and action + self.classname = client.classname + self.template_type = client.template_type + + # extract the index display information from the form + self.columns = {} + if self.form.has_key(':columns'): + for entry in handleListCGIValue(self.form[':columns']): + self.columns[entry] = 1 + self.sort = [] + if self.form.has_key(':sort'): + self.sort = handleListCGIValue(self.form[':sort']) + self.group = [] + if self.form.has_key(':group'): + self.group = handleListCGIValue(self.form[':group']) + self.filter = [] + if self.form.has_key(':filter'): + self.filter = handleListCGIValue(self.form[':filter']) + self.filterspec = {} + for name in self.filter: + if self.form.has_key(name): + self.filterspec[name]=handleListCGIValue(self.form[name]) + + def __str__(self): + d = {} + d.update(self.__dict__) + f = '' + for k in self.form.keys(): + f += '\n %r=%r'%(k,handleListCGIValue(self.form[k])) + d['form'] = f + e = '' + for k,v in self.env.items(): + e += '\n %r=%r'%(k, v) + d['env'] = e + return ''' +form: %(form)s +base: %(base)r +classname: %(classname)r +template_type: %(template_type)r +columns: %(columns)r +sort: %(sort)r +group: %(group)r +filter: %(filter)r +filterspec: %(filterspec)r +env: %(env)s +'''%d + + def indexargs_form(self): + ''' return the current index args as form elements ''' + l = [] + s = '<input type="hidden" name="%s" value="%s">' + if 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: + l.append(s%(':filter', ','.join(self.filter))) + 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.filter: + l.append(':filter=%s'%(','.join(self.filter))) + for k,v in self.filterspec.items(): + l.append('%s=%s'%(k, ','.join(v))) + return '%s?%s'%(url, '&'.join(l)) + + def base_javascript(self): + return ''' +<script language="javascript"> +submitted = false; +function submit_once() { + if (submitted) { + alert("Your request is being processed.\\nPlease be patient."); + return 0; + } + submitted = true; + return 1; +} + +function help_window(helpurl, width, height) { + HelpWin = window.open('%s/' + helpurl, 'RoundupHelpWindow', 'scrollbars=yes,resizable=yes,toolbar=no,height='+height+',width='+width); +} +</script> +'''%self.base + + def batch(self): + ''' Return a batch object for results from the "current search" + ''' + filterspec = self.filterspec + sort = self.sort + group = self.group + + # get the list of ids we're batching over + klass = self.client.db.getclass(self.classname) + l = klass.filter(None, filterspec, sort, group) + + # figure batch args + if self.form.has_key(':pagesize'): + size = int(self.form[':pagesize'].value) + else: + size = 50 + if self.form.has_key(':startwith'): + start = int(self.form[':startwith'].value) + else: + start = 0 + + # return the batch object + return Batch(self.client, self.classname, l, size, start) + +class Batch(ZTUtils.Batch): + def __init__(self, client, classname, l, size, start, end=0, orphan=0, overlap=0): + self.client = client + self.classname = classname + ZTUtils.Batch.__init__(self, l, size, start, end, orphan, overlap) + + # overwrite so we can late-instantiate the HTMLItem instance + def __getitem__(self, index): + if index < 0: + if index + self.end < self.first: raise IndexError, index + return self._sequence[index + self.end] + + if index >= self.length: raise IndexError, index + + # wrap the return in an HTMLItem + return HTMLItem(self.client.db, self.classname, + self._sequence[index+self.first]) + + # 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, + self.first - self._size + self.overlap, 0, self.orphan, + self.overlap) + + def next(self): + try: + self._sequence[self.end] + except IndexError: + return None + return Batch(self.client, self.classname, self._sequence, self._size, + self.end - self.overlap, 0, self.orphan, self.overlap) + + def length(self): + self.sequence_length = l = len(self._sequence) + return l +
