1 import hyperdb, date, password
2 from i18n import _
3 import htmltemplate
4 import cgi, os, StringIO, urllib, types
7 def do_plain(client, classname, cl, props, nodeid, filterspec, property, escape=0, lookup=1):
8 ''' display a String property directly;
10 display a Date property in a specified time zone with an option to
11 omit the time from the date stamp;
13 for a Link or Multilink property, display the key strings of the
14 linked nodes (or the ids if the linked class has no key property)
15 when the lookup argument is true, otherwise just return the
16 linked ids
17 '''
18 if not nodeid and client.form is None:
19 return _('[Field: not called from item]')
20 propclass = props[property]
21 value = determine_value(cl, props, nodeid, filterspec, property)
23 if isinstance(propclass, hyperdb.Password):
24 value = _('*encrypted*')
25 elif isinstance(propclass, hyperdb.Boolean):
26 value = value and "Yes" or "No"
27 elif isinstance(propclass, hyperdb.Link):
28 if value:
29 if lookup:
30 linkcl = client.db.classes[propclass.classname]
31 k = linkcl.labelprop(1)
32 value = linkcl.get(value, k)
33 else:
34 value = _('[unselected]')
35 elif isinstance(propclass, hyperdb.Multilink):
36 if value:
37 if lookup:
38 linkcl = client.db.classes[propclass.classname]
39 k = linkcl.labelprop(1)
40 labels = []
41 for v in value:
42 labels.append(linkcl.get(v, k))
43 value = ', '.join(labels)
44 else:
45 value = ', '.join(value)
46 else:
47 value = ''
48 else:
49 value = str(value)
51 if escape:
52 value = cgi.escape(value)
53 return value
55 def do_stext(client, classname, cl, props, nodeid, filterspec, property, escape=0):
56 '''Render as structured text using the StructuredText module
57 (see above for details)
58 '''
59 s = do_plain(client, classname, cl, props, nodeid, filterspec, property, escape=escape)
60 if not StructuredText:
61 return s
62 return StructuredText(s,level=1,header=0)
64 def determine_value(cl, props, nodeid, filterspec, property):
65 '''determine the value of a property using the node, form or
66 filterspec
67 '''
68 if nodeid:
69 value = cl.get(nodeid, property, None)
70 if value is None:
71 if isinstance(props[property], hyperdb.Multilink):
72 return []
73 return ''
74 return value
75 elif filterspec is not None:
76 if isinstance(props[property], hyperdb.Multilink):
77 return filterspec.get(property, [])
78 else:
79 return filterspec.get(property, '')
80 # TODO: pull the value from the form
81 if isinstance(props[property], hyperdb.Multilink):
82 return []
83 else:
84 return ''
86 def make_sort_function(client, filterspec, classname):
87 '''Make a sort function for a given class
88 '''
89 linkcl = client.db.getclass(classname)
90 if linkcl.getprops().has_key('order'):
91 sort_on = 'order'
92 else:
93 sort_on = linkcl.labelprop()
94 def sortfunc(a, b, linkcl=linkcl, sort_on=sort_on):
95 return cmp(linkcl.get(a, sort_on), linkcl.get(b, sort_on))
96 return sortfunc
98 def do_field(client, classname, cl, props, nodeid, filterspec, property, size=None, showid=0):
99 ''' display a property like the plain displayer, but in a text field
100 to be edited
102 Note: if you would prefer an option list style display for
103 link or multilink editing, use menu().
104 '''
105 if not nodeid and client.form is None and filterspec is None:
106 return _('[Field: not called from item]')
107 if size is None:
108 size = 30
110 propclass = props[property]
112 # get the value
113 value = determine_value(cl, props, nodeid, filterspec, property)
114 # now display
115 if (isinstance(propclass, hyperdb.String) or
116 isinstance(propclass, hyperdb.Date) or
117 isinstance(propclass, hyperdb.Interval)):
118 if value is None:
119 value = ''
120 else:
121 value = cgi.escape(str(value))
122 value = '"'.join(value.split('"'))
123 s = '<input name="%s" value="%s" size="%s">'%(property, value, size)
124 elif isinstance(propclass, hyperdb.Boolean):
125 checked = value and "checked" or ""
126 s = '<input type="radio" name="%s" value="yes" %s>Yes'%(property, checked)
127 if checked:
128 checked = ""
129 else:
130 checked = "checked"
131 s += '<input type="radio" name="%s" value="no" %s>No'%(property, checked)
132 elif isinstance(propclass, hyperdb.Number):
133 s = '<input name="%s" value="%s" size="%s">'%(property, value, size)
134 elif isinstance(propclass, hyperdb.Password):
135 s = '<input type="password" name="%s" size="%s">'%(property, size)
136 elif isinstance(propclass, hyperdb.Link):
137 linkcl = client.db.getclass(propclass.classname)
138 if linkcl.getprops().has_key('order'):
139 sort_on = 'order'
140 else:
141 sort_on = linkcl.labelprop()
142 options = linkcl.filter(None, {}, [sort_on], [])
143 # TODO: make this a field display, not a menu one!
144 l = ['<select name="%s">'%property]
145 k = linkcl.labelprop(1)
146 if value is None:
147 s = 'selected '
148 else:
149 s = ''
150 l.append(_('<option %svalue="-1">- no selection -</option>')%s)
151 for optionid in options:
152 option = linkcl.get(optionid, k)
153 s = ''
154 if optionid == value:
155 s = 'selected '
156 if showid:
157 lab = '%s%s: %s'%(propclass.classname, optionid, option)
158 else:
159 lab = option
160 if size is not None and len(lab) > size:
161 lab = lab[:size-3] + '...'
162 lab = cgi.escape(lab)
163 l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
164 l.append('</select>')
165 s = '\n'.join(l)
166 elif isinstance(propclass, hyperdb.Multilink):
167 sortfunc = make_sort_function(client, filterspec, propclass.classname)
168 linkcl = client.db.getclass(propclass.classname)
169 if value:
170 value.sort(sortfunc)
171 # map the id to the label property
172 if not showid:
173 k = linkcl.labelprop(1)
174 value = [linkcl.get(v, k) for v in value]
175 value = cgi.escape(','.join(value))
176 s = '<input name="%s" size="%s" value="%s">'%(property, size, value)
177 else:
178 s = _('Plain: bad propclass "%(propclass)s"')%locals()
179 return s
181 def do_multiline(client, classname, cl, props, nodeid, filterspec, property, rows=5, cols=40):
182 ''' display a string property in a multiline text edit field
183 '''
184 if not nodeid and client.form is None and filterspec is None:
185 return _('[Multiline: not called from item]')
187 propclass = props[property]
189 # make sure this is a link property
190 if not isinstance(propclass, hyperdb.String):
191 return _('[Multiline: not a string]')
193 # get the value
194 value = determine_value(cl, props, nodeid, filterspec, property)
195 if value is None:
196 value = ''
198 # display
199 return '<textarea name="%s" rows="%s" cols="%s">%s</textarea>'%(
200 property, rows, cols, value)
202 def do_menu(client, classname, cl, props, nodeid, filterspec, property, size=None, height=None, showid=0,
203 additional=[], **conditions):
204 ''' For a Link/Multilink property, display a menu of the available
205 choices
207 If the additional properties are specified, they will be
208 included in the text of each option in (brackets, with, commas).
209 '''
210 if not nodeid and client.form is None and filterspec is None:
211 return _('[Field: not called from item]')
213 propclass = props[property]
215 # make sure this is a link property
216 if not (isinstance(propclass, hyperdb.Link) or
217 isinstance(propclass, hyperdb.Multilink)):
218 return _('[Menu: not a link]')
220 # sort function
221 sortfunc = make_sort_function(client, filterspec, propclass.classname)
223 # get the value
224 value = determine_value(cl, props, nodeid, filterspec, property)
226 # display
227 if isinstance(propclass, hyperdb.Multilink):
228 linkcl = client.db.getclass(propclass.classname)
229 if linkcl.getprops().has_key('order'):
230 sort_on = 'order'
231 else:
232 sort_on = linkcl.labelprop()
233 options = linkcl.filter(None, conditions, [sort_on], [])
234 height = height or min(len(options), 7)
235 l = ['<select multiple name="%s" size="%s">'%(property, height)]
236 k = linkcl.labelprop(1)
237 for optionid in options:
238 option = linkcl.get(optionid, k)
239 s = ''
240 if optionid in value or option in value:
241 s = 'selected '
242 if showid:
243 lab = '%s%s: %s'%(propclass.classname, optionid, option)
244 else:
245 lab = option
246 if size is not None and len(lab) > size:
247 lab = lab[:size-3] + '...'
248 if additional:
249 m = []
250 for propname in additional:
251 m.append(linkcl.get(optionid, propname))
252 lab = lab + ' (%s)'%', '.join(m)
253 lab = cgi.escape(lab)
254 l.append('<option %svalue="%s">%s</option>'%(s, optionid,
255 lab))
256 l.append('</select>')
257 return '\n'.join(l)
258 if isinstance(propclass, hyperdb.Link):
259 # force the value to be a single choice
260 if type(value) is types.ListType:
261 value = value[0]
262 linkcl = client.db.getclass(propclass.classname)
263 l = ['<select name="%s">'%property]
264 k = linkcl.labelprop(1)
265 s = ''
266 if value is None:
267 s = 'selected '
268 l.append(_('<option %svalue="-1">- no selection -</option>')%s)
269 if linkcl.getprops().has_key('order'):
270 sort_on = 'order'
271 else:
272 sort_on = linkcl.labelprop()
273 options = linkcl.filter(None, conditions, [sort_on], [])
274 for optionid in options:
275 option = linkcl.get(optionid, k)
276 s = ''
277 if value in [optionid, option]:
278 s = 'selected '
279 if showid:
280 lab = '%s%s: %s'%(propclass.classname, optionid, option)
281 else:
282 lab = option
283 if size is not None and len(lab) > size:
284 lab = lab[:size-3] + '...'
285 if additional:
286 m = []
287 for propname in additional:
288 m.append(linkcl.get(optionid, propname))
289 lab = lab + ' (%s)'%', '.join(map(str, m))
290 lab = cgi.escape(lab)
291 l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
292 l.append('</select>')
293 return '\n'.join(l)
294 return _('[Menu: not a link]')
296 #XXX deviates from spec
297 def do_link(client, classname, cl, props, nodeid, filterspec, property=None, is_download=0, showid=0):
298 '''For a Link or Multilink property, display the names of the linked
299 nodes, hyperlinked to the item views on those nodes.
300 For other properties, link to this node with the property as the
301 text.
303 If is_download is true, append the property value to the generated
304 URL so that the link may be used as a download link and the
305 downloaded file name is correct.
306 '''
307 if not nodeid and client.form is None:
308 return _('[Link: not called from item]')
310 # get the value
311 value = determine_value(cl, props, nodeid, filterspec, property)
312 propclass = props[property]
313 if isinstance(propclass, hyperdb.Boolean):
314 value = value and "Yes" or "No"
315 elif isinstance(propclass, hyperdb.Link):
316 if value in ('', None, []):
317 return _('[no %(propname)s]')%{'propname':property.capitalize()}
318 linkname = propclass.classname
319 linkcl = client.db.getclass(linkname)
320 k = linkcl.labelprop(1)
321 linkvalue = cgi.escape(str(linkcl.get(value, k)))
322 if showid:
323 label = value
324 title = ' title="%s"'%linkvalue
325 # note ... this should be urllib.quote(linkcl.get(value, k))
326 else:
327 label = linkvalue
328 title = ''
329 if is_download:
330 return '<a href="%s%s/%s"%s>%s</a>'%(linkname, value,
331 linkvalue, title, label)
332 else:
333 return '<a href="%s%s"%s>%s</a>'%(linkname, value, title, label)
334 elif isinstance(propclass, hyperdb.Multilink):
335 if value in ('', None, []):
336 return _('[no %(propname)s]')%{'propname':property.capitalize()}
337 linkname = propclass.classname
338 linkcl = client.db.getclass(linkname)
339 k = linkcl.labelprop(1)
340 l = []
341 for value in value:
342 linkvalue = cgi.escape(str(linkcl.get(value, k)))
343 if showid:
344 label = value
345 title = ' title="%s"'%linkvalue
346 # note ... this should be urllib.quote(linkcl.get(value, k))
347 else:
348 label = linkvalue
349 title = ''
350 if is_download:
351 l.append('<a href="%s%s/%s"%s>%s</a>'%(linkname, value,
352 linkvalue, title, label))
353 else:
354 l.append('<a href="%s%s"%s>%s</a>'%(linkname, value,
355 title, label))
356 return ', '.join(l)
357 if is_download:
358 if value in ('', None, []):
359 return _('[no %(propname)s]')%{'propname':property.capitalize()}
360 return '<a href="%s%s/%s">%s</a>'%(classname, nodeid,
361 value, value)
362 else:
363 if value in ('', None, []):
364 value = _('[no %(propname)s]')%{'propname':property.capitalize()}
365 return '<a href="%s%s">%s</a>'%(classname, nodeid, value)
367 def do_count(client, classname, cl, props, nodeid, filterspec, property, **args):
368 ''' for a Multilink property, display a count of the number of links in
369 the list
370 '''
371 if not nodeid:
372 return _('[Count: not called from item]')
374 propclass = props[property]
375 if not isinstance(propclass, hyperdb.Multilink):
376 return _('[Count: not a Multilink]')
378 # figure the length then...
379 value = cl.get(nodeid, property)
380 return str(len(value))
382 # XXX pretty is definitely new ;)
383 def do_reldate(client, classname, cl, props, nodeid, filterspec, property, pretty=0):
384 ''' display a Date property in terms of an interval relative to the
385 current date (e.g. "+ 3w", "- 2d").
387 with the 'pretty' flag, make it pretty
388 '''
389 if not nodeid and client.form is None:
390 return _('[Reldate: not called from item]')
392 propclass = props[property]
393 if not isinstance(propclass, hyperdb.Date):
394 return _('[Reldate: not a Date]')
396 if nodeid:
397 value = cl.get(nodeid, property)
398 else:
399 return ''
400 if not value:
401 return ''
403 # figure the interval
404 interval = date.Date('.') - value
405 if pretty:
406 if not nodeid:
407 return _('now')
408 return interval.pretty()
409 return str(interval)
411 def do_download(client, classname, cl, props, nodeid, filterspec, property, **args):
412 ''' show a Link("file") or Multilink("file") property using links that
413 allow you to download files
414 '''
415 if not nodeid:
416 return _('[Download: not called from item]')
417 return do_link(client, classname, cl, props, nodeid, filterspec, property, is_download=1)
420 def do_checklist(client, classname, cl, props, nodeid, filterspec, property, sortby=None):
421 ''' for a Link or Multilink property, display checkboxes for the
422 available choices to permit filtering
424 sort the checklist by the argument (+/- property name)
425 '''
426 propclass = props[property]
427 if (not isinstance(propclass, hyperdb.Link) and not
428 isinstance(propclass, hyperdb.Multilink)):
429 return _('[Checklist: not a link]')
431 # get our current checkbox state
432 if nodeid:
433 # get the info from the node - make sure it's a list
434 if isinstance(propclass, hyperdb.Link):
435 value = [cl.get(nodeid, property)]
436 else:
437 value = cl.get(nodeid, property)
438 elif filterspec is not None:
439 # get the state from the filter specification (always a list)
440 value = filterspec.get(property, [])
441 else:
442 # it's a new node, so there's no state
443 value = []
445 # so we can map to the linked node's "lable" property
446 linkcl = client.db.getclass(propclass.classname)
447 l = []
448 k = linkcl.labelprop(1)
450 # build list of options and then sort it, either
451 # by id + label or <sortby>-value + label;
452 # a minus reverses the sort order, while + or no
453 # prefix sort in increasing order
454 reversed = 0
455 if sortby:
456 if sortby[0] == '-':
457 reversed = 1
458 sortby = sortby[1:]
459 elif sortby[0] == '+':
460 sortby = sortby[1:]
461 options = []
462 for optionid in linkcl.list():
463 if sortby:
464 sortval = linkcl.get(optionid, sortby)
465 else:
466 sortval = int(optionid)
467 option = cgi.escape(str(linkcl.get(optionid, k)))
468 options.append((sortval, option, optionid))
469 options.sort()
470 if reversed:
471 options.reverse()
473 # build checkboxes
474 for sortval, option, optionid in options:
475 if optionid in value or option in value:
476 checked = 'checked'
477 else:
478 checked = ''
479 l.append('%s:<input type="checkbox" %s name="%s" value="%s">'%(
480 option, checked, property, option))
482 # for Links, allow the "unselected" option too
483 if isinstance(propclass, hyperdb.Link):
484 if value is None or '-1' in value:
485 checked = 'checked'
486 else:
487 checked = ''
488 l.append(_('[unselected]:<input type="checkbox" %s name="%s" '
489 'value="-1">')%(checked, property))
490 return '\n'.join(l)
492 def do_note(client, classname, cl, props, nodeid, filterspec, rows=5, cols=80):
493 ''' display a "note" field, which is a text area for entering a note to
494 go along with a change.
495 '''
496 # TODO: pull the value from the form
497 return '<textarea name="__note" wrap="hard" rows=%s cols=%s>'\
498 '</textarea>'%(rows, cols)
500 # XXX new function
501 def do_list(client, classname, cl, props, nodeid, filterspec, property, reverse=0, xtracols=None):
502 ''' list the items specified by property using the standard index for
503 the class
504 '''
505 propcl = props[property]
506 if not isinstance(propcl, hyperdb.Multilink):
507 return _('[List: not a Multilink]')
509 value = determine_value(cl, props, nodeid, filterspec, property)
510 if not value:
511 return ''
513 # sort, possibly revers and then re-stringify
514 value = map(int, value)
515 value.sort()
516 if reverse:
517 value.reverse()
518 value = map(str, value)
520 # render the sub-index into a string
521 fp = StringIO.StringIO()
522 try:
523 write_save = client.write
524 client.write = fp.write
525 client.listcontext = ('%s%s' % (classname, nodeid), property)
526 index = htmltemplate.IndexTemplate(client, client.instance.TEMPLATES, propcl.classname)
527 index.render(nodeids=value, show_display_form=0, xtracols=xtracols)
528 finally:
529 client.listcontext = None
530 client.write = write_save
532 return fp.getvalue()
534 # XXX new function
535 def do_history(client, classname, cl, props, nodeid, filterspec, direction='descending'):
536 ''' list the history of the item
538 If "direction" is 'descending' then the most recent event will
539 be displayed first. If it is 'ascending' then the oldest event
540 will be displayed first.
541 '''
542 if nodeid is None:
543 return _("[History: node doesn't exist]")
545 l = ['<table width=100% border=0 cellspacing=0 cellpadding=2>',
546 '<tr class="list-header">',
547 _('<th align=left><span class="list-item">Date</span></th>'),
548 _('<th align=left><span class="list-item">User</span></th>'),
549 _('<th align=left><span class="list-item">Action</span></th>'),
550 _('<th align=left><span class="list-item">Args</span></th>'),
551 '</tr>']
552 comments = {}
553 history = cl.history(nodeid)
554 history.sort()
555 if direction == 'descending':
556 history.reverse()
557 for id, evt_date, user, action, args in history:
558 date_s = str(evt_date).replace("."," ")
559 arg_s = ''
560 if action == 'link' and type(args) == type(()):
561 if len(args) == 3:
562 linkcl, linkid, key = args
563 arg_s += '<a href="%s%s">%s%s %s</a>'%(linkcl, linkid,
564 linkcl, linkid, key)
565 else:
566 arg_s = str(args)
568 elif action == 'unlink' and type(args) == type(()):
569 if len(args) == 3:
570 linkcl, linkid, key = args
571 arg_s += '<a href="%s%s">%s%s %s</a>'%(linkcl, linkid,
572 linkcl, linkid, key)
573 else:
574 arg_s = str(args)
576 elif type(args) == type({}):
577 cell = []
578 for k in args.keys():
579 # try to get the relevant property and treat it
580 # specially
581 try:
582 prop = props[k]
583 except:
584 prop = None
585 if prop is not None:
586 if args[k] and (isinstance(prop, hyperdb.Multilink) or
587 isinstance(prop, hyperdb.Link)):
588 # figure what the link class is
589 classname = prop.classname
590 try:
591 linkcl = client.db.getclass(classname)
592 except KeyError:
593 labelprop = None
594 comments[classname] = _('''The linked class
595 %(classname)s no longer exists''')%locals()
596 labelprop = linkcl.labelprop(1)
597 hrefable = os.path.exists(
598 os.path.join(client.instance.TEMPLATES, classname+'.item'))
600 if isinstance(prop, hyperdb.Multilink) and \
601 len(args[k]) > 0:
602 ml = []
603 for linkid in args[k]:
604 label = classname + linkid
605 # if we have a label property, try to use it
606 # TODO: test for node existence even when
607 # there's no labelprop!
608 try:
609 if labelprop is not None:
610 label = linkcl.get(linkid, labelprop)
611 except IndexError:
612 comments['no_link'] = _('''<strike>The
613 linked node no longer
614 exists</strike>''')
615 ml.append('<strike>%s</strike>'%label)
616 else:
617 if hrefable:
618 ml.append('<a href="%s%s">%s</a>'%(
619 classname, linkid, label))
620 else:
621 ml.append(label)
622 cell.append('%s:\n %s'%(k, ',\n '.join(ml)))
623 elif isinstance(prop, hyperdb.Link) and args[k]:
624 label = classname + args[k]
625 # if we have a label property, try to use it
626 # TODO: test for node existence even when
627 # there's no labelprop!
628 if labelprop is not None:
629 try:
630 label = linkcl.get(args[k], labelprop)
631 except IndexError:
632 comments['no_link'] = _('''<strike>The
633 linked node no longer
634 exists</strike>''')
635 cell.append(' <strike>%s</strike>,\n'%label)
636 # "flag" this is done .... euwww
637 label = None
638 if label is not None:
639 if hrefable:
640 cell.append('%s: <a href="%s%s">%s</a>\n'%(k,
641 classname, args[k], label))
642 else:
643 cell.append('%s: %s' % (k,label))
645 elif isinstance(prop, hyperdb.Date) and args[k]:
646 d = date.Date(args[k])
647 cell.append('%s: %s'%(k, str(d)))
649 elif isinstance(prop, hyperdb.Interval) and args[k]:
650 d = date.Interval(args[k])
651 cell.append('%s: %s'%(k, str(d)))
653 elif isinstance(prop, hyperdb.String) and args[k]:
654 cell.append('%s: %s'%(k, cgi.escape(args[k])))
656 elif not args[k]:
657 cell.append('%s: (no value)\n'%k)
659 else:
660 cell.append('%s: %s\n'%(k, str(args[k])))
661 else:
662 # property no longer exists
663 comments['no_exist'] = _('''<em>The indicated property
664 no longer exists</em>''')
665 cell.append('<em>%s: %s</em>\n'%(k, str(args[k])))
666 arg_s = '<br />'.join(cell)
667 else:
668 # unkown event!!
669 comments['unknown'] = _('''<strong><em>This event is not
670 handled by the history display!</em></strong>''')
671 arg_s = '<strong><em>' + str(args) + '</em></strong>'
672 date_s = date_s.replace(' ', ' ')
673 l.append('<tr><td nowrap valign=top>%s</td><td valign=top>%s</td>'
674 '<td valign=top>%s</td><td valign=top>%s</td></tr>'%(date_s,
675 user, action, arg_s))
676 if comments:
677 l.append(_('<tr><td colspan=4><strong>Note:</strong></td></tr>'))
678 for entry in comments.values():
679 l.append('<tr><td colspan=4>%s</td></tr>'%entry)
680 l.append('</table>')
681 return '\n'.join(l)
683 # XXX new function
684 def do_submit(client, classname, cl, props, nodeid, filterspec, value=None):
685 ''' add a submit button for the item
686 '''
687 if value is None:
688 if nodeid:
689 value = "Submit Changes"
690 else:
691 value = "Submit New Entry"
692 if nodeid or client.form is not None:
693 return _('<input type="submit" name="submit" value="%s">' % value)
694 else:
695 return _('[Submit: not called from item]')
697 def do_classhelp(client, classname, cl, props, nodeid, filterspec, clname, properties, label='?', width='400',
698 height='400'):
699 '''pop up a javascript window with class help
701 This generates a link to a popup window which displays the
702 properties indicated by "properties" of the class named by
703 "classname". The "properties" should be a comma-separated list
704 (eg. 'id,name,description').
706 You may optionally override the label displayed, the width and
707 height. The popup window will be resizable and scrollable.
708 '''
709 return '<a href="javascript:help_window(\'classhelp?classname=%s&' \
710 'properties=%s\', \'%s\', \'%s\')"><b>(%s)</b></a>'%(clname,
711 properties, width, height, label)
713 def do_email(client, classname, cl, props, nodeid, filterspec, property, escape=0):
714 '''display the property as one or more "fudged" email addrs
715 '''
717 if not nodeid and client.form is None:
718 return _('[Email: not called from item]')
719 propclass = props[property]
720 if nodeid:
721 # get the value for this property
722 try:
723 value = cl.get(nodeid, property)
724 except KeyError:
725 # a KeyError here means that the node doesn't have a value
726 # for the specified property
727 value = ''
728 else:
729 value = ''
730 if isinstance(propclass, hyperdb.String):
731 if value is None: value = ''
732 else: value = str(value)
733 value = value.replace('@', ' at ')
734 value = value.replace('.', ' ')
735 else:
736 value = _('[Email: not a string]')%locals()
737 if escape:
738 value = cgi.escape(value)
739 return value
741 def do_filterspec(client, classname, cl, props, nodeid, filterspec, classprop, urlprop):
742 qs = cl.get(nodeid, urlprop)
743 classname = cl.get(nodeid, classprop)
744 filterspec = {}
745 query = cgi.parse_qs(qs)
746 for k,v in query.items():
747 query[k] = v[0].split(',')
748 pagesize = query.get(':pagesize',['25'])[0]
749 search_text = query.get('search_text', [''])[0]
750 search_text = urllib.unquote(search_text)
751 for k,v in query.items():
752 if k[0] != ':':
753 filterspec[k] = v
754 ixtmplt = htmltemplate.IndexTemplate(client, client.instance.TEMPLATES, classname)
755 qform = '<form onSubmit="return submit_once()" action="%s%s">\n'%(
756 classname,nodeid)
757 qform += ixtmplt.filter_form(search_text,
758 query.get(':filter', []),
759 query.get(':columns', []),
760 query.get(':group', []),
761 [],
762 query.get(':sort',[]),
763 filterspec,
764 pagesize)
765 return qform + '</table>\n'
767 def do_href(client, classname, cl, props, nodeid, filterspec, property, prefix='', suffix='', label=''):
768 value = determine_value(cl, props, nodeid, filterspec, property)
769 return '<a href="%s%s%s">%s</a>' % (prefix, value, suffix, label)
771 def do_remove(client, classname, cl, props, nodeid, filterspec):
772 ''' put a remove href for an item in a list '''
773 if not nodeid:
774 return _('[Remove not called from item]')
775 try:
776 parentdesignator, mlprop = client.listcontext
777 except (AttributeError, TypeError):
778 return _('[Remove not called form listing of multilink]')
779 return '<a href="remove?:target=%s%s&:multilink=%s:%s">[Remove]</a>' % (classname, nodeid, parentdesignator, mlprop)