Code

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