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