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