Code

Fixed handling of passed-in values in form elements (ie. during a
[roundup.git] / roundup / htmltemplate.py
1 # $Id: htmltemplate.py,v 1.9 2001-07-29 08:27:40 richard Exp $
3 import os, re, StringIO, urllib, cgi, errno
5 import hyperdb, date
7 class Base:
8     def __init__(self, db, templates, classname, nodeid=None, form=None,
9             filterspec=None):
10         # TODO: really not happy with the way templates is passed on here
11         self.db, self.templates = db, templates
12         self.classname, self.nodeid = classname, nodeid
13         self.form, self.filterspec = form, filterspec
14         self.cl = self.db.classes[self.classname]
15         self.properties = self.cl.getprops()
17 class Plain(Base):
18     ''' display a String property directly;
20         display a Date property in a specified time zone with an option to
21         omit the time from the date stamp;
23         for a Link or Multilink property, display the key strings of the
24         linked nodes (or the ids if the linked class has no key property)
25     '''
26     def __call__(self, property):
27         if not self.nodeid and self.form is None:
28             return '[Field: not called from item]'
29         propclass = self.properties[property]
30         if self.nodeid:
31             value = self.cl.get(self.nodeid, property)
32         else:
33             # TODO: pull the value from the form
34             if propclass.isMultilinkType: value = []
35             else: value = ''
36         if propclass.isStringType:
37             if value is None: value = ''
38             else: value = str(value)
39         elif propclass.isDateType:
40             value = str(value)
41         elif propclass.isIntervalType:
42             value = str(value)
43         elif propclass.isLinkType:
44             linkcl = self.db.classes[propclass.classname]
45             k = linkcl.labelprop()
46             if value: value = str(linkcl.get(value, k))
47             else: value = '[unselected]'
48         elif propclass.isMultilinkType:
49             linkcl = self.db.classes[propclass.classname]
50             k = linkcl.labelprop()
51             value = ', '.join([linkcl.get(i, k) for i in value])
52         else:
53             s = 'Plain: bad propclass "%s"'%propclass
54         return value
56 class Field(Base):
57     ''' display a property like the plain displayer, but in a text field
58         to be edited
59     '''
60     def __call__(self, property, size=None, height=None, showid=0):
61         if not self.nodeid and self.form and self.filterspec is None:
62             return '[Field: not called from item]'
63         propclass = self.properties[property]
64         if self.nodeid:
65             value = self.cl.get(self.nodeid, property)
66         elif self.filterspec is not None:
67             if propclass.isMultilinkType:
68                 value = self.filterspec.get(property, [])
69             else:
70                 value = self.filterspec.get(property, '')
71         else:
72             # TODO: pull the value from the form
73             if propclass.isMultilinkType: value = []
74             else: value = ''
75         if (propclass.isStringType or propclass.isDateType or
76                 propclass.isIntervalType):
77             size = size or 30
78             if value is None:
79                 value = ''
80             else:
81                 value = cgi.escape(value)
82                 value = '"'.join(value.split('"'))
83             s = '<input name="%s" value="%s" size="%s">'%(property, value, size)
84         elif propclass.isLinkType:
85             linkcl = self.db.classes[propclass.classname]
86             l = ['<select name="%s">'%property]
87             k = linkcl.labelprop()
88             for optionid in linkcl.list():
89                 option = linkcl.get(optionid, k)
90                 s = ''
91                 if optionid == value:
92                     s = 'selected '
93                 if showid:
94                     lab = '%s%s: %s'%(propclass.classname, optionid, option)
95                 else:
96                     lab = option
97                 if size is not None and len(lab) > size:
98                     lab = lab[:size-3] + '...'
99                 l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
100             l.append('</select>')
101             s = '\n'.join(l)
102         elif propclass.isMultilinkType:
103             linkcl = self.db.classes[propclass.classname]
104             list = linkcl.list()
105             height = height or min(len(list), 7)
106             l = ['<select multiple name="%s" size="%s">'%(property, height)]
107             k = linkcl.labelprop()
108             for optionid in list:
109                 option = linkcl.get(optionid, k)
110                 s = ''
111                 if optionid in value:
112                     s = 'selected '
113                 if showid:
114                     lab = '%s%s: %s'%(propclass.classname, optionid, option)
115                 else:
116                     lab = option
117                 if size is not None and len(lab) > size:
118                     lab = lab[:size-3] + '...'
119                 l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
120             l.append('</select>')
121             s = '\n'.join(l)
122         else:
123             s = 'Plain: bad propclass "%s"'%propclass
124         return s
126 class Menu(Base):
127     ''' for a Link property, display a menu of the available choices
128     '''
129     def __call__(self, property, size=None, height=None, showid=0):
130         propclass = self.properties[property]
131         if self.nodeid:
132             value = self.cl.get(self.nodeid, property)
133         else:
134             # TODO: pull the value from the form
135             if propclass.isMultilinkType: value = []
136             else: value = None
137         if propclass.isLinkType:
138             linkcl = self.db.classes[propclass.classname]
139             l = ['<select name="%s">'%property]
140             k = linkcl.labelprop()
141             for optionid in linkcl.list():
142                 option = linkcl.get(optionid, k)
143                 s = ''
144                 if optionid == value:
145                     s = 'selected '
146                 l.append('<option %svalue="%s">%s</option>'%(s, optionid, option))
147             l.append('</select>')
148             return '\n'.join(l)
149         if propclass.isMultilinkType:
150             linkcl = self.db.classes[propclass.classname]
151             list = linkcl.list()
152             height = height or min(len(list), 7)
153             l = ['<select multiple name="%s" size="%s">'%(property, height)]
154             k = linkcl.labelprop()
155             for optionid in list:
156                 option = linkcl.get(optionid, k)
157                 s = ''
158                 if optionid in value:
159                     s = 'selected '
160                 if showid:
161                     lab = '%s%s: %s'%(propclass.classname, optionid, option)
162                 else:
163                     lab = option
164                 if size is not None and len(lab) > size:
165                     lab = lab[:size-3] + '...'
166                 l.append('<option %svalue="%s">%s</option>'%(s, optionid, option))
167             l.append('</select>')
168             return '\n'.join(l)
169         return '[Menu: not a link]'
171 #XXX deviates from spec
172 class Link(Base):
173     ''' for a Link or Multilink property, display the names of the linked
174         nodes, hyperlinked to the item views on those nodes
175         for other properties, link to this node with the property as the text
176     '''
177     def __call__(self, property=None, **args):
178         if not self.nodeid and self.form is None:
179             return '[Link: not called from item]'
180         propclass = self.properties[property]
181         if self.nodeid:
182             value = self.cl.get(self.nodeid, property)
183         else:
184             if propclass.isMultilinkType: value = []
185             else: value = ''
186         if propclass.isLinkType:
187             if value is None:
188                 return '[not assigned]'
189             linkcl = self.db.classes[propclass.classname]
190             k = linkcl.labelprop()
191             linkvalue = linkcl.get(value, k)
192             return '<a href="%s%s">%s</a>'%(linkcl, value, linkvalue)
193         if propclass.isMultilinkType:
194             linkcl = self.db.classes[propclass.classname]
195             k = linkcl.labelprop()
196             l = []
197             for value in value:
198                 linkvalue = linkcl.get(value, k)
199                 l.append('<a href="%s%s">%s</a>'%(linkcl, value, linkvalue))
200             return ', '.join(l)
201         return '<a href="%s%s">%s</a>'%(self.classname, self.nodeid, value)
203 class Count(Base):
204     ''' for a Multilink property, display a count of the number of links in
205         the list
206     '''
207     def __call__(self, property, **args):
208         if not self.nodeid:
209             return '[Count: not called from item]'
210         propclass = self.properties[property]
211         value = self.cl.get(self.nodeid, property)
212         if propclass.isMultilinkType:
213             return str(len(value))
214         return '[Count: not a Multilink]'
216 # XXX pretty is definitely new ;)
217 class Reldate(Base):
218     ''' display a Date property in terms of an interval relative to the
219         current date (e.g. "+ 3w", "- 2d").
221         with the 'pretty' flag, make it pretty
222     '''
223     def __call__(self, property, pretty=0):
224         if not self.nodeid and self.form is None:
225             return '[Reldate: not called from item]'
226         propclass = self.properties[property]
227         if not propclass.isDateType:
228             return '[Reldate: not a Date]'
229         if self.nodeid:
230             value = self.cl.get(self.nodeid, property)
231         else:
232             value = date.Date('.')
233         interval = value - date.Date('.')
234         if pretty:
235             if not self.nodeid:
236                 return 'now'
237             pretty = interval.pretty()
238             if pretty is None:
239                 pretty = value.pretty()
240             return pretty
241         return str(interval)
243 class Download(Base):
244     ''' show a Link("file") or Multilink("file") property using links that
245         allow you to download files
246     '''
247     def __call__(self, property, **args):
248         if not self.nodeid:
249             return '[Download: not called from item]'
250         propclass = self.properties[property]
251         value = self.cl.get(self.nodeid, property)
252         if propclass.isLinkType:
253             linkcl = self.db.classes[propclass.classname]
254             linkvalue = linkcl.get(value, k)
255             return '<a href="%s%s">%s</a>'%(linkcl, value, linkvalue)
256         if propclass.isMultilinkType:
257             linkcl = self.db.classes[propclass.classname]
258             l = []
259             for value in value:
260                 linkvalue = linkcl.get(value, k)
261                 l.append('<a href="%s%s">%s</a>'%(linkcl, value, linkvalue))
262             return ', '.join(l)
263         return '[Download: not a link]'
266 class Checklist(Base):
267     ''' for a Link or Multilink property, display checkboxes for the available
268         choices to permit filtering
269     '''
270     def __call__(self, property, **args):
271         propclass = self.properties[property]
272         if self.nodeid:
273             value = self.cl.get(self.nodeid, property)
274         elif self.filterspec is not None:
275             value = self.filterspec.get(property, [])
276         else:
277             value = []
278         if propclass.isLinkType or propclass.isMultilinkType:
279             linkcl = self.db.classes[propclass.classname]
280             l = []
281             k = linkcl.labelprop()
282             for optionid in linkcl.list():
283                 option = linkcl.get(optionid, k)
284                 if optionid in value or option in value:
285                     checked = 'checked'
286                 else:
287                     checked = ''
288                 l.append('%s:<input type="checkbox" %s name="%s" value="%s">'%(
289                     option, checked, propclass.classname, option))
290             return '\n'.join(l)
291         return '[Checklist: not a link]'
293 class Note(Base):
294     ''' display a "note" field, which is a text area for entering a note to
295         go along with a change. 
296     '''
297     def __call__(self, rows=5, cols=80):
298        # TODO: pull the value from the form
299         return '<textarea name="__note" rows=%s cols=%s></textarea>'%(rows,
300             cols)
302 # XXX new function
303 class List(Base):
304     ''' list the items specified by property using the standard index for
305         the class
306     '''
307     def __call__(self, property, **args):
308         propclass = self.properties[property]
309         if not propclass.isMultilinkType:
310             return '[List: not a Multilink]'
311         fp = StringIO.StringIO()
312         args['show_display_form'] = 0
313         value = self.cl.get(self.nodeid, property)
314         # TODO: really not happy with the way templates is passed on here
315         index(fp, self.templates, self.db, propclass.classname, nodeids=value,
316             show_display_form=0)
317         return fp.getvalue()
319 # XXX new function
320 class History(Base):
321     ''' list the history of the item
322     '''
323     def __call__(self, **args):
324         l = ['<table width=100% border=0 cellspacing=0 cellpadding=2>',
325             '<tr class="list-header">',
326             '<td><span class="list-item"><strong>Date</strong></span></td>',
327             '<td><span class="list-item"><strong>User</strong></span></td>',
328             '<td><span class="list-item"><strong>Action</strong></span></td>',
329             '<td><span class="list-item"><strong>Args</strong></span></td>']
331         for id, date, user, action, args in self.cl.history(self.nodeid):
332             l.append('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>'%(
333                date, user, action, args))
334         l.append('</table>')
335         return '\n'.join(l)
337 # XXX new function
338 class Submit(Base):
339     ''' add a submit button for the item
340     '''
341     def __call__(self):
342         if self.nodeid:
343             return '<input type="submit" value="Submit Changes">'
344         elif self.form is not None:
345             return '<input type="submit" value="Submit New Entry">'
346         else:
347             return '[Submit: not called from item]'
351 #   INDEX TEMPLATES
353 class IndexTemplateReplace:
354     def __init__(self, globals, locals, props):
355         self.globals = globals
356         self.locals = locals
357         self.props = props
359     def go(self, text, replace=re.compile(
360             r'((<property\s+name="(?P<name>[^>]+)">(?P<text>.+?)</property>)|'
361             r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I|re.S)):
362         return replace.sub(self, text)
363         
364     def __call__(self, m, filter=None, columns=None, sort=None, group=None):
365         if m.group('name'):
366             if m.group('name') in self.props:
367                 text = m.group('text')
368                 replace = IndexTemplateReplace(self.globals, {}, self.props)
369                 return replace.go(m.group('text'))
370             else:
371                 return ''
372         if m.group('display'):
373             command = m.group('command')
374             return eval(command, self.globals, self.locals)
375         print '*** unhandled match', m.groupdict()
377 def sortby(sort_name, columns, filter, sort, group, filterspec):
378     l = []
379     w = l.append
380     for k, v in filterspec.items():
381         k = urllib.quote(k)
382         if type(v) == type([]):
383             w('%s=%s'%(k, ','.join(map(urllib.quote, v))))
384         else:
385             w('%s=%s'%(k, urllib.quote(v)))
386     if columns:
387         w(':columns=%s'%','.join(map(urllib.quote, columns)))
388     if filter:
389         w(':filter=%s'%','.join(map(urllib.quote, filter)))
390     if group:
391         w(':group=%s'%','.join(map(urllib.quote, group)))
392     m = []
393     s_dir = ''
394     for name in sort:
395         dir = name[0]
396         if dir == '-':
397             dir = ''
398         else:
399             name = name[1:]
400         if sort_name == name:
401             if dir == '':
402                 s_dir = '-'
403             elif dir == '-':
404                 s_dir = ''
405         else:
406             m.append(dir+urllib.quote(name))
407     m.insert(0, s_dir+urllib.quote(sort_name))
408     # so things don't get completely out of hand, limit the sort to two columns
409     w(':sort=%s'%','.join(m[:2]))
410     return '&'.join(l)
412 def index(client, templates, db, classname, filterspec={}, filter=[],
413         columns=[], sort=[], group=[], show_display_form=1, nodeids=None,
414         col_re=re.compile(r'<property\s+name="([^>]+)">')):
415     globals = {
416         'plain': Plain(db, templates, classname, filterspec=filterspec),
417         'field': Field(db, templates, classname, filterspec=filterspec),
418         'menu': Menu(db, templates, classname, filterspec=filterspec),
419         'link': Link(db, templates, classname, filterspec=filterspec),
420         'count': Count(db, templates, classname, filterspec=filterspec),
421         'reldate': Reldate(db, templates, classname, filterspec=filterspec),
422         'download': Download(db, templates, classname, filterspec=filterspec),
423         'checklist': Checklist(db, templates, classname, filterspec=filterspec),
424         'list': List(db, templates, classname, filterspec=filterspec),
425         'history': History(db, templates, classname, filterspec=filterspec),
426         'submit': Submit(db, templates, classname, filterspec=filterspec),
427         'note': Note(db, templates, classname, filterspec=filterspec)
428     }
429     cl = db.classes[classname]
430     properties = cl.getprops()
431     w = client.write
432     w('<form>')
434     try:
435         template = open(os.path.join(templates, classname+'.filter')).read()
436         all_filters = col_re.findall(template)
437     except IOError, error:
438         if error.errno != errno.ENOENT: raise
439         template = None
440         all_filters = []
441     if template and filter:
442         # display the filter section
443         w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
444         w('<tr class="location-bar">')
445         w(' <th align="left" colspan="2">Filter specification...</th>')
446         w('</tr>')
447         replace = IndexTemplateReplace(globals, locals(), filter)
448         w(replace.go(template))
449         w('<tr class="location-bar"><td width="1%%">&nbsp;</td>')
450         w('<td><input type="submit" value="Redisplay"></td></tr>')
451         w('</table>')
453     # If the filters aren't being displayed, then hide their current
454     # value in the form
455     if not filter:
456         for k, v in filterspec.items():
457             if type(v) == type([]): v = ','.join(v)
458             w('<input type="hidden" name="%s" value="%s">'%(k, v))
460     # make sure that the sorting doesn't get lost either
461     if sort:
462         w('<input type="hidden" name=":sort" value="%s">'%','.join(sort))
464     # XXX deviate from spec here ...
465     # load the index section template and figure the default columns from it
466     template = open(os.path.join(templates, classname+'.index')).read()
467     all_columns = col_re.findall(template)
468     if not columns:
469         columns = []
470         for name in all_columns:
471             columns.append(name)
472     else:
473         # re-sort columns to be the same order as all_columns
474         l = []
475         for name in all_columns:
476             if name in columns:
477                 l.append(name)
478         columns = l
480     # now display the index section
481     w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
482     w('<tr class="list-header">')
483     for name in columns:
484         cname = name.capitalize()
485         if show_display_form:
486             anchor = "%s?%s"%(classname, sortby(name, columns, filter,
487                 sort, group, filterspec))
488             w('<td><span class="list-item"><a href="%s">%s</a></span></td>'%(
489                 anchor, cname))
490         else:
491             w('<td><span class="list-item">%s</span></td>'%cname)
492     w('</tr>')
494     # this stuff is used for group headings - optimise the group names
495     old_group = None
496     group_names = []
497     if group:
498         for name in group:
499             if name[0] == '-': group_names.append(name[1:])
500             else: group_names.append(name)
502     # now actually loop through all the nodes we get from the filter and
503     # apply the template
504     if nodeids is None:
505         nodeids = cl.filter(filterspec, sort, group)
506     for nodeid in nodeids:
507         # check for a group heading
508         if group_names:
509             this_group = [cl.get(nodeid, name) for name in group_names]
510             if this_group != old_group:
511                 l = []
512                 for name in group_names:
513                     prop = properties[name]
514                     if prop.isLinkType:
515                         group_cl = db.classes[prop.classname]
516                         key = group_cl.getkey()
517                         value = cl.get(nodeid, name)
518                         if value is None:
519                             l.append('[unselected %s]'%prop.classname)
520                         else:
521                             l.append(group_cl.get(cl.get(nodeid, name), key))
522                     elif prop.isMultilinkType:
523                         group_cl = db.classes[prop.classname]
524                         key = group_cl.getkey()
525                         for value in cl.get(nodeid, name):
526                             l.append(group_cl.get(value, key))
527                     else:
528                         value = cl.get(nodeid, name)
529                         if value is None:
530                             value = '[empty %s]'%name
531                         l.append(value)
532                 w('<tr class="section-bar">'
533                   '<td align=middle colspan=%s><strong>%s</strong></td></tr>'%(
534                     len(columns), ', '.join(l)))
535                 old_group = this_group
537         # display this node's row
538         for value in globals.values():
539             if hasattr(value, 'nodeid'):
540                 value.nodeid = nodeid
541         replace = IndexTemplateReplace(globals, locals(), columns)
542         w(replace.go(template))
544     w('</table>')
546     if not show_display_form:
547         return
549     # now add in the filter/columns/group/etc config table form
550     w('<p>')
551     w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
552     names = []
553     for name in cl.getprops().keys():
554         if name in all_filters or name in all_columns:
555             names.append(name)
556     w('<tr class="location-bar">')
557     w('<th align="left" colspan=%s>View customisation...</th></tr>'%
558         (len(names)+1))
559     w('<tr class="location-bar"><th>&nbsp;</th>')
560     for name in names:
561         w('<th>%s</th>'%name.capitalize())
562     w('</tr>')
564     # filter
565     if all_filters:
566         w('<tr><th width="1%" align=right class="location-bar">Filters</th>')
567         for name in names:
568             if name not in all_filters:
569                 w('<td>&nbsp;</td>')
570                 continue
571             if name in filter: checked=' checked'
572             else: checked=''
573             w('<td align=middle>')
574             w('<input type="checkbox" name=":filter" value="%s" %s></td>'%(name,
575                 checked))
576         w('</tr>')
578     # columns
579     if all_columns:
580         w('<tr><th width="1%" align=right class="location-bar">Columns</th>')
581         for name in names:
582             if name not in all_columns:
583                 w('<td>&nbsp;</td>')
584                 continue
585             if name in columns: checked=' checked'
586             else: checked=''
587             w('<td align=middle>')
588             w('<input type="checkbox" name=":columns" value="%s" %s></td>'%(
589                 name, checked))
590         w('</tr>')
592         # group
593         w('<tr><th width="1%" align=right class="location-bar">Grouping</th>')
594         for name in names:
595             prop = properties[name]
596             if name not in all_columns:
597                 w('<td>&nbsp;</td>')
598                 continue
599             if name in group: checked=' checked'
600             else: checked=''
601             w('<td align=middle>')
602             w('<input type="checkbox" name=":group" value="%s" %s></td>'%(
603                 name, checked))
604         w('</tr>')
606     w('<tr class="location-bar"><td width="1%">&nbsp;</td>')
607     w('<td colspan="%s">'%len(names))
608     w('<input type="submit" value="Redisplay"></td></tr>')
609     w('</table>')
610     w('</form>')
614 #   ITEM TEMPLATES
616 class ItemTemplateReplace:
617     def __init__(self, globals, locals, cl, nodeid):
618         self.globals = globals
619         self.locals = locals
620         self.cl = cl
621         self.nodeid = nodeid
623     def go(self, text, replace=re.compile(
624             r'((<property\s+name="(?P<name>[^>]+)">(?P<text>.+?)</property>)|'
625             r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I|re.S)):
626         return replace.sub(self, text)
628     def __call__(self, m, filter=None, columns=None, sort=None, group=None):
629         if m.group('name'):
630             if self.nodeid and self.cl.get(self.nodeid, m.group('name')):
631                 replace = ItemTemplateReplace(self.globals, {}, self.cl,
632                     self.nodeid)
633                 return replace.go(m.group('text'))
634             else:
635                 return ''
636         if m.group('display'):
637             command = m.group('command')
638             return eval(command, self.globals, self.locals)
639         print '*** unhandled match', m.groupdict()
641 def item(client, templates, db, classname, nodeid, replace=re.compile(
642             r'((?P<prop><property\s+name="(?P<propname>[^>]+)">)|'
643             r'(?P<endprop></property>)|'
644             r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I)):
646     globals = {
647         'plain': Plain(db, templates, classname, nodeid),
648         'field': Field(db, templates, classname, nodeid),
649         'menu': Menu(db, templates, classname, nodeid),
650         'link': Link(db, templates, classname, nodeid),
651         'count': Count(db, templates, classname, nodeid),
652         'reldate': Reldate(db, templates, classname, nodeid),
653         'download': Download(db, templates, classname, nodeid),
654         'checklist': Checklist(db, templates, classname, nodeid),
655         'list': List(db, templates, classname, nodeid),
656         'history': History(db, templates, classname, nodeid),
657         'submit': Submit(db, templates, classname, nodeid),
658         'note': Note(db, templates, classname, nodeid)
659     }
661     cl = db.classes[classname]
662     properties = cl.getprops()
664     if properties.has_key('type') and properties.has_key('content'):
665         pass
666         # XXX we really want to return this as a downloadable...
667         #  currently I handle this at a higher level by detecting 'file'
668         #  designators...
670     w = client.write
671     w('<form action="%s%s">'%(classname, nodeid))
672     s = open(os.path.join(templates, classname+'.item')).read()
673     replace = ItemTemplateReplace(globals, locals(), cl, nodeid)
674     w(replace.go(s))
675     w('</form>')
678 def newitem(client, templates, db, classname, form, replace=re.compile(
679             r'((?P<prop><property\s+name="(?P<propname>[^>]+)">)|'
680             r'(?P<endprop></property>)|'
681             r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I)):
682     globals = {
683         'plain': Plain(db, templates, classname, form=form),
684         'field': Field(db, templates, classname, form=form),
685         'menu': Menu(db, templates, classname, form=form),
686         'link': Link(db, templates, classname, form=form),
687         'count': Count(db, templates, classname, form=form),
688         'reldate': Reldate(db, templates, classname, form=form),
689         'download': Download(db, templates, classname, form=form),
690         'checklist': Checklist(db, templates, classname, form=form),
691         'list': List(db, templates, classname, form=form),
692         'history': History(db, templates, classname, form=form),
693         'submit': Submit(db, templates, classname, form=form),
694         'note': Note(db, templates, classname, form=form)
695     }
697     cl = db.classes[classname]
698     properties = cl.getprops()
700     w = client.write
701     try:
702         s = open(os.path.join(templates, classname+'.newitem')).read()
703     except:
704         s = open(os.path.join(templates, classname+'.item')).read()
705     w('<form action="new%s">'%classname)
706     replace = ItemTemplateReplace(globals, locals(), None, None)
707     w(replace.go(s))
708     w('</form>')
711 # $Log: not supported by cvs2svn $
712 # Revision 1.8  2001/07/29 07:01:39  richard
713 # Added vim command to all source so that we don't get no steenkin' tabs :)
715 # Revision 1.7  2001/07/29 05:36:14  richard
716 # Cleanup of the link label generation.
718 # Revision 1.6  2001/07/29 04:06:42  richard
719 # Fixed problem in link display when Link value is None.
721 # Revision 1.5  2001/07/28 08:17:09  richard
722 # fixed use of stylesheet
724 # Revision 1.4  2001/07/28 07:59:53  richard
725 # Replaced errno integers with their module values.
726 # De-tabbed templatebuilder.py
728 # Revision 1.3  2001/07/25 03:39:47  richard
729 # Hrm - displaying links to classes that don't specify a key property. I've
730 # got it defaulting to 'name', then 'title' and then a "random" property (first
731 # one returned by getprops().keys().
732 # Needs to be moved onto the Class I think...
734 # Revision 1.2  2001/07/22 12:09:32  richard
735 # Final commit of Grande Splite
737 # Revision 1.1  2001/07/22 11:58:35  richard
738 # More Grande Splite
741 # vim: set filetype=python ts=4 sw=4 et si