cdbdc3fb7d4993af0e8b686e60a7ff5607b4cfb3
1 # $Id: htmltemplate.py,v 1.3 2001-07-25 03:39:47 richard Exp $
3 import os, re, StringIO, urllib, cgi
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 linkcl = self.db.classes[propclass.classname]
242 k = linkcl.getkey()
243 # if the linked-to class doesn't have a key property, then try
244 # 'name', then 'title' and then just use a random one.
245 if not k:
246 linkprops = linkcl.getprops()
247 if linkprops.has_key('name'):
248 k = 'name'
249 elif linkprops.has_key('title'):
250 k = 'title'
251 else:
252 k = linkprops.keys()[0]
253 linkvalue = linkcl.get(value, k)
254 return '<a href="%s%s">%s</a>'%(linkcl, value, linkvalue)
255 if propclass.isMultilinkType:
256 linkcl = self.db.classes[propclass.classname]
257 k = linkcl.getkey()
258 # if the linked-to class doesn't have a key property, then try
259 # 'name', then 'title' and then just use a random one.
260 if not k:
261 linkprops = linkcl.getprops()
262 if linkprops.has_key('name'):
263 k = 'name'
264 elif linkprops.has_key('title'):
265 k = 'title'
266 else:
267 k = linkprops.keys()[0]
268 l = []
269 for value in value:
270 linkvalue = linkcl.get(value, k)
271 l.append('<a href="%s%s">%s</a>'%(linkcl, value, linkvalue))
272 return ', '.join(l)
273 return '<a href="%s%s">%s</a>'%(self.classname, self.nodeid, value)
275 class Count(Base):
276 ''' for a Multilink property, display a count of the number of links in
277 the list
278 '''
279 def __call__(self, property, **args):
280 if not self.nodeid:
281 return '[Count: not called from item]'
282 propclass = self.properties[property]
283 value = self.cl.get(self.nodeid, property)
284 if propclass.isMultilinkType:
285 return str(len(value))
286 return '[Count: not a Multilink]'
288 # XXX pretty is definitely new ;)
289 class Reldate(Base):
290 ''' display a Date property in terms of an interval relative to the
291 current date (e.g. "+ 3w", "- 2d").
293 with the 'pretty' flag, make it pretty
294 '''
295 def __call__(self, property, pretty=0):
296 if not self.nodeid and self.form is None:
297 return '[Reldate: not called from item]'
298 propclass = self.properties[property]
299 if not propclass.isDateType:
300 return '[Reldate: not a Date]'
301 if self.nodeid:
302 value = self.cl.get(self.nodeid, property)
303 else:
304 value = date.Date('.')
305 interval = value - date.Date('.')
306 if pretty:
307 if not self.nodeid:
308 return 'now'
309 pretty = interval.pretty()
310 if pretty is None:
311 pretty = value.pretty()
312 return pretty
313 return str(interval)
315 class Download(Base):
316 ''' show a Link("file") or Multilink("file") property using links that
317 allow you to download files
318 '''
319 def __call__(self, property, **args):
320 if not self.nodeid:
321 return '[Download: not called from item]'
322 propclass = self.properties[property]
323 value = self.cl.get(self.nodeid, property)
324 if propclass.isLinkType:
325 linkcl = self.db.classes[propclass.classname]
326 linkvalue = linkcl.get(value, k)
327 return '<a href="%s%s">%s</a>'%(linkcl, value, linkvalue)
328 if propclass.isMultilinkType:
329 linkcl = self.db.classes[propclass.classname]
330 l = []
331 for value in value:
332 linkvalue = linkcl.get(value, k)
333 l.append('<a href="%s%s">%s</a>'%(linkcl, value, linkvalue))
334 return ', '.join(l)
335 return '[Download: not a link]'
338 class Checklist(Base):
339 ''' for a Link or Multilink property, display checkboxes for the available
340 choices to permit filtering
341 '''
342 def __call__(self, property, **args):
343 propclass = self.properties[property]
344 if self.nodeid:
345 value = self.cl.get(self.nodeid, property)
346 else:
347 value = []
348 if propclass.isLinkType or propclass.isMultilinkType:
349 linkcl = self.db.classes[propclass.classname]
350 l = []
351 k = linkcl.getkey()
352 # if the linked-to class doesn't have a key property, then try
353 # 'name', then 'title' and then just use a random one.
354 if not k:
355 linkprops = linkcl.getprops()
356 if linkprops.has_key('name'):
357 k = 'name'
358 elif linkprops.has_key('title'):
359 k = 'title'
360 else:
361 k = linkprops.keys()[0]
362 for optionid in linkcl.list():
363 option = linkcl.get(optionid, k)
364 if optionid in value:
365 checked = 'checked'
366 else:
367 checked = ''
368 l.append('%s:<input type="checkbox" %s name="%s" value="%s">'%(
369 option, checked, propclass.classname, option))
370 return '\n'.join(l)
371 return '[Checklist: not a link]'
373 class Note(Base):
374 ''' display a "note" field, which is a text area for entering a note to
375 go along with a change.
376 '''
377 def __call__(self, rows=5, cols=80):
378 # TODO: pull the value from the form
379 return '<textarea name="__note" rows=%s cols=%s></textarea>'%(rows,
380 cols)
382 # XXX new function
383 class List(Base):
384 ''' list the items specified by property using the standard index for
385 the class
386 '''
387 def __call__(self, property, **args):
388 propclass = self.properties[property]
389 if not propclass.isMultilinkType:
390 return '[List: not a Multilink]'
391 fp = StringIO.StringIO()
392 args['show_display_form'] = 0
393 value = self.cl.get(self.nodeid, property)
394 # TODO: really not happy with the way templates is passed on here
395 index(fp, self.templates, self.db, propclass.classname, nodeids=value,
396 show_display_form=0)
397 return fp.getvalue()
399 # XXX new function
400 class History(Base):
401 ''' list the history of the item
402 '''
403 def __call__(self, **args):
404 l = ['<table width=100% border=0 cellspacing=0 cellpadding=2>',
405 '<tr class="list-header">',
406 '<td><span class="list-item"><strong>Date</strong></span></td>',
407 '<td><span class="list-item"><strong>User</strong></span></td>',
408 '<td><span class="list-item"><strong>Action</strong></span></td>',
409 '<td><span class="list-item"><strong>Args</strong></span></td>']
411 for id, date, user, action, args in self.cl.history(self.nodeid):
412 l.append('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>'%(
413 date, user, action, args))
414 l.append('</table>')
415 return '\n'.join(l)
417 # XXX new function
418 class Submit(Base):
419 ''' add a submit button for the item
420 '''
421 def __call__(self):
422 if self.nodeid:
423 return '<input type="submit" value="Submit Changes">'
424 elif self.form is not None:
425 return '<input type="submit" value="Submit New Entry">'
426 else:
427 return '[Submit: not called from item]'
430 #
431 # INDEX TEMPLATES
432 #
433 class IndexTemplateReplace:
434 def __init__(self, globals, locals, props):
435 self.globals = globals
436 self.locals = locals
437 self.props = props
439 def go(self, text, replace=re.compile(
440 r'((<property\s+name="(?P<name>[^>]+)">(?P<text>.+?)</property>)|'
441 r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I|re.S)):
442 return replace.sub(self, text)
444 def __call__(self, m, filter=None, columns=None, sort=None, group=None):
445 if m.group('name'):
446 if m.group('name') in self.props:
447 text = m.group('text')
448 replace = IndexTemplateReplace(self.globals, {}, self.props)
449 return replace.go(m.group('text'))
450 else:
451 return ''
452 if m.group('display'):
453 command = m.group('command')
454 return eval(command, self.globals, self.locals)
455 print '*** unhandled match', m.groupdict()
457 def sortby(sort_name, columns, filter, sort, group, filterspec):
458 l = []
459 w = l.append
460 for k, v in filterspec.items():
461 k = urllib.quote(k)
462 if type(v) == type([]):
463 w('%s=%s'%(k, ','.join(map(urllib.quote, v))))
464 else:
465 w('%s=%s'%(k, urllib.quote(v)))
466 if columns:
467 w(':columns=%s'%','.join(map(urllib.quote, columns)))
468 if filter:
469 w(':filter=%s'%','.join(map(urllib.quote, filter)))
470 if group:
471 w(':group=%s'%','.join(map(urllib.quote, group)))
472 m = []
473 s_dir = ''
474 for name in sort:
475 dir = name[0]
476 if dir == '-':
477 dir = ''
478 else:
479 name = name[1:]
480 if sort_name == name:
481 if dir == '':
482 s_dir = '-'
483 elif dir == '-':
484 s_dir = ''
485 else:
486 m.append(dir+urllib.quote(name))
487 m.insert(0, s_dir+urllib.quote(sort_name))
488 # so things don't get completely out of hand, limit the sort to two columns
489 w(':sort=%s'%','.join(m[:2]))
490 return '&'.join(l)
492 def index(client, templates, db, classname, filterspec={}, filter=[],
493 columns=[], sort=[], group=[], show_display_form=1, nodeids=None,
494 col_re=re.compile(r'<property\s+name="([^>]+)">')):
495 globals = {
496 'plain': Plain(db, templates, classname, form={}),
497 'field': Field(db, templates, classname, form={}),
498 'menu': Menu(db, templates, classname, form={}),
499 'link': Link(db, templates, classname, form={}),
500 'count': Count(db, templates, classname, form={}),
501 'reldate': Reldate(db, templates, classname, form={}),
502 'download': Download(db, templates, classname, form={}),
503 'checklist': Checklist(db, templates, classname, form={}),
504 'list': List(db, templates, classname, form={}),
505 'history': History(db, templates, classname, form={}),
506 'submit': Submit(db, templates, classname, form={}),
507 'note': Note(db, templates, classname, form={})
508 }
509 cl = db.classes[classname]
510 properties = cl.getprops()
511 w = client.write
513 try:
514 template = open(os.path.join(templates, classname+'.filter')).read()
515 all_filters = col_re.findall(template)
516 except IOError, error:
517 if error.errno != 2: raise
518 template = None
519 all_filters = []
520 if template and filter:
521 # display the filter section
522 w('<form>')
523 w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
524 w('<tr class="location-bar">')
525 w(' <th align="left" colspan="2">Filter specification...</th>')
526 w('</tr>')
527 replace = IndexTemplateReplace(globals, locals(), filter)
528 w(replace.go(template))
529 if columns:
530 w('<input type="hidden" name=":columns" value="%s">'%','.join(columns))
531 if filter:
532 w('<input type="hidden" name=":filter" value="%s">'%','.join(filter))
533 if sort:
534 w('<input type="hidden" name=":sort" value="%s">'%','.join(sort))
535 if group:
536 w('<input type="hidden" name=":group" value="%s">'%','.join(group))
537 for k, v in filterspec.items():
538 if type(v) == type([]): v = ','.join(v)
539 w('<input type="hidden" name="%s" value="%s">'%(k, v))
540 w('<tr class="location-bar"><td width="1%%"> </td>')
541 w('<td><input type="submit" value="Redisplay"></td></tr>')
542 w('</table>')
543 w('</form>')
545 # XXX deviate from spec here ...
546 # load the index section template and figure the default columns from it
547 template = open(os.path.join(templates, classname+'.index')).read()
548 all_columns = col_re.findall(template)
549 if not columns:
550 columns = []
551 for name in all_columns:
552 columns.append(name)
553 else:
554 # re-sort columns to be the same order as all_columns
555 l = []
556 for name in all_columns:
557 if name in columns:
558 l.append(name)
559 columns = l
561 # now display the index section
562 w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
563 w('<tr class="list-header">')
564 for name in columns:
565 cname = name.capitalize()
566 if show_display_form:
567 anchor = "%s?%s"%(classname, sortby(name, columns, filter,
568 sort, group, filterspec))
569 w('<td><span class="list-item"><a href="%s">%s</a></span></td>'%(
570 anchor, cname))
571 else:
572 w('<td><span class="list-item">%s</span></td>'%cname)
573 w('</tr>')
575 # this stuff is used for group headings - optimise the group names
576 old_group = None
577 group_names = []
578 if group:
579 for name in group:
580 if name[0] == '-': group_names.append(name[1:])
581 else: group_names.append(name)
583 # now actually loop through all the nodes we get from the filter and
584 # apply the template
585 if nodeids is None:
586 nodeids = cl.filter(filterspec, sort, group)
587 for nodeid in nodeids:
588 # check for a group heading
589 if group_names:
590 this_group = [cl.get(nodeid, name) for name in group_names]
591 if this_group != old_group:
592 l = []
593 for name in group_names:
594 prop = properties[name]
595 if prop.isLinkType:
596 group_cl = db.classes[prop.classname]
597 key = group_cl.getkey()
598 value = cl.get(nodeid, name)
599 if value is None:
600 l.append('[unselected %s]'%prop.classname)
601 else:
602 l.append(group_cl.get(cl.get(nodeid, name), key))
603 elif prop.isMultilinkType:
604 group_cl = db.classes[prop.classname]
605 key = group_cl.getkey()
606 for value in cl.get(nodeid, name):
607 l.append(group_cl.get(value, key))
608 else:
609 value = cl.get(nodeid, name)
610 if value is None:
611 value = '[empty %s]'%name
612 l.append(value)
613 w('<tr class="list-header">'
614 '<td align=left colspan=%s><strong>%s</strong></td></tr>'%(
615 len(columns), ', '.join(l)))
616 old_group = this_group
618 # display this node's row
619 for value in globals.values():
620 if hasattr(value, 'nodeid'):
621 value.nodeid = nodeid
622 replace = IndexTemplateReplace(globals, locals(), columns)
623 w(replace.go(template))
625 w('</table>')
627 if not show_display_form:
628 return
630 # now add in the filter/columns/group/etc config table form
631 w('<p><form>')
632 w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
633 for k,v in filterspec.items():
634 if type(v) == type([]): v = ','.join(v)
635 w('<input type="hidden" name="%s" value="%s">'%(k, v))
636 if sort:
637 w('<input type="hidden" name=":sort" value="%s">'%','.join(sort))
638 names = []
639 for name in cl.getprops().keys():
640 if name in all_filters or name in all_columns:
641 names.append(name)
642 w('<tr class="location-bar">')
643 w('<th align="left" colspan=%s>View customisation...</th></tr>'%
644 (len(names)+1))
645 w('<tr class="location-bar"><th> </th>')
646 for name in names:
647 w('<th>%s</th>'%name.capitalize())
648 w('</tr>')
650 # filter
651 if all_filters:
652 w('<tr><th width="1%" align=right class="location-bar">Filters</th>')
653 for name in names:
654 if name not in all_filters:
655 w('<td> </td>')
656 continue
657 if name in filter: checked=' checked'
658 else: checked=''
659 w('<td align=middle>')
660 w('<input type="checkbox" name=":filter" value="%s" %s></td>'%(name,
661 checked))
662 w('</tr>')
664 # columns
665 if all_columns:
666 w('<tr><th width="1%" align=right class="location-bar">Columns</th>')
667 for name in names:
668 if name not in all_columns:
669 w('<td> </td>')
670 continue
671 if name in columns: checked=' checked'
672 else: checked=''
673 w('<td align=middle>')
674 w('<input type="checkbox" name=":columns" value="%s" %s></td>'%(
675 name, checked))
676 w('</tr>')
678 # group
679 w('<tr><th width="1%" align=right class="location-bar">Grouping</th>')
680 for name in names:
681 prop = properties[name]
682 if name not in all_columns:
683 w('<td> </td>')
684 continue
685 if name in group: checked=' checked'
686 else: checked=''
687 w('<td align=middle>')
688 w('<input type="checkbox" name=":group" value="%s" %s></td>'%(
689 name, checked))
690 w('</tr>')
692 w('<tr class="location-bar"><td width="1%"> </td>')
693 w('<td colspan="%s">'%len(names))
694 w('<input type="submit" value="Redisplay"></td></tr>')
695 w('</table>')
696 w('</form>')
699 #
700 # ITEM TEMPLATES
701 #
702 class ItemTemplateReplace:
703 def __init__(self, globals, locals, cl, nodeid):
704 self.globals = globals
705 self.locals = locals
706 self.cl = cl
707 self.nodeid = nodeid
709 def go(self, text, replace=re.compile(
710 r'((<property\s+name="(?P<name>[^>]+)">(?P<text>.+?)</property>)|'
711 r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I|re.S)):
712 return replace.sub(self, text)
714 def __call__(self, m, filter=None, columns=None, sort=None, group=None):
715 if m.group('name'):
716 if self.nodeid and self.cl.get(self.nodeid, m.group('name')):
717 replace = ItemTemplateReplace(self.globals, {}, self.cl,
718 self.nodeid)
719 return replace.go(m.group('text'))
720 else:
721 return ''
722 if m.group('display'):
723 command = m.group('command')
724 return eval(command, self.globals, self.locals)
725 print '*** unhandled match', m.groupdict()
727 def item(client, templates, db, classname, nodeid, replace=re.compile(
728 r'((?P<prop><property\s+name="(?P<propname>[^>]+)">)|'
729 r'(?P<endprop></property>)|'
730 r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I)):
732 globals = {
733 'plain': Plain(db, templates, classname, nodeid),
734 'field': Field(db, templates, classname, nodeid),
735 'menu': Menu(db, templates, classname, nodeid),
736 'link': Link(db, templates, classname, nodeid),
737 'count': Count(db, templates, classname, nodeid),
738 'reldate': Reldate(db, templates, classname, nodeid),
739 'download': Download(db, templates, classname, nodeid),
740 'checklist': Checklist(db, templates, classname, nodeid),
741 'list': List(db, templates, classname, nodeid),
742 'history': History(db, templates, classname, nodeid),
743 'submit': Submit(db, templates, classname, nodeid),
744 'note': Note(db, templates, classname, nodeid)
745 }
747 cl = db.classes[classname]
748 properties = cl.getprops()
750 if properties.has_key('type') and properties.has_key('content'):
751 pass
752 # XXX we really want to return this as a downloadable...
753 # currently I handle this at a higher level by detecting 'file'
754 # designators...
756 w = client.write
757 w('<form action="%s%s">'%(classname, nodeid))
758 s = open(os.path.join(templates, classname+'.item')).read()
759 replace = ItemTemplateReplace(globals, locals(), cl, nodeid)
760 w(replace.go(s))
761 w('</form>')
764 def newitem(client, templates, db, classname, form, replace=re.compile(
765 r'((?P<prop><property\s+name="(?P<propname>[^>]+)">)|'
766 r'(?P<endprop></property>)|'
767 r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I)):
768 globals = {
769 'plain': Plain(db, templates, classname, form=form),
770 'field': Field(db, templates, classname, form=form),
771 'menu': Menu(db, templates, classname, form=form),
772 'link': Link(db, templates, classname, form=form),
773 'count': Count(db, templates, classname, form=form),
774 'reldate': Reldate(db, templates, classname, form=form),
775 'download': Download(db, templates, classname, form=form),
776 'checklist': Checklist(db, templates, classname, form=form),
777 'list': List(db, templates, classname, form=form),
778 'history': History(db, templates, classname, form=form),
779 'submit': Submit(db, templates, classname, form=form),
780 'note': Note(db, templates, classname, form=form)
781 }
783 cl = db.classes[classname]
784 properties = cl.getprops()
786 w = client.write
787 try:
788 s = open(os.path.join(templates, classname+'.newitem')).read()
789 except:
790 s = open(os.path.join(templates, classname+'.item')).read()
791 w('<form action="new%s">'%classname)
792 replace = ItemTemplateReplace(globals, locals(), None, None)
793 w(replace.go(s))
794 w('</form>')
796 #
797 # $Log: not supported by cvs2svn $
798 # Revision 1.2 2001/07/22 12:09:32 richard
799 # Final commit of Grande Splite
800 #
801 # Revision 1.1 2001/07/22 11:58:35 richard
802 # More Grande Splite
803 #