7d574fdec53276788732de73072dea0051c6fd4c
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]'
350 #
351 # INDEX TEMPLATES
352 #
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)
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%%"> </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> </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> </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> </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> </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%"> </td>')
607 w('<td colspan="%s">'%len(names))
608 w('<input type="submit" value="Redisplay"></td></tr>')
609 w('</table>')
610 w('</form>')
613 #
614 # ITEM TEMPLATES
615 #
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>')
710 #
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 :)
714 #
715 # Revision 1.7 2001/07/29 05:36:14 richard
716 # Cleanup of the link label generation.
717 #
718 # Revision 1.6 2001/07/29 04:06:42 richard
719 # Fixed problem in link display when Link value is None.
720 #
721 # Revision 1.5 2001/07/28 08:17:09 richard
722 # fixed use of stylesheet
723 #
724 # Revision 1.4 2001/07/28 07:59:53 richard
725 # Replaced errno integers with their module values.
726 # De-tabbed templatebuilder.py
727 #
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...
733 #
734 # Revision 1.2 2001/07/22 12:09:32 richard
735 # Final commit of Grande Splite
736 #
737 # Revision 1.1 2001/07/22 11:58:35 richard
738 # More Grande Splite
739 #
740 #
741 # vim: set filetype=python ts=4 sw=4 et si