index 036ad190cc67a1b66a9b85bbd05aab1c6b07672f..3d9d69e70b3933f21ab964327230d44135fe302f 100644 (file)
--- a/roundup/htmltemplate.py
+++ b/roundup/htmltemplate.py
-# $Id: htmltemplate.py,v 1.8 2001-07-29 07:01:39 richard Exp $
+#
+# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
+# This module is free software, and you may redistribute it and/or modify
+# under the same terms as Python, so long as this copyright message and
+# disclaimer are retained in their original form.
+#
+# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
+# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
+# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
+# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+#
+# $Id: htmltemplate.py,v 1.21 2001-08-16 07:34:59 richard Exp $
import os, re, StringIO, urllib, cgi, errno
import hyperdb, date
class Base:
- def __init__(self, db, templates, classname, nodeid=None, form=None):
+ def __init__(self, db, templates, classname, nodeid=None, form=None,
+ filterspec=None):
# TODO: really not happy with the way templates is passed on here
self.db, self.templates = db, templates
self.classname, self.nodeid = classname, nodeid
- self.form = form
+ self.form, self.filterspec = form, filterspec
self.cl = self.db.classes[self.classname]
self.properties = self.cl.getprops()
value = self.cl.get(self.nodeid, property)
else:
# TODO: pull the value from the form
- if propclass.isMultilinkType: value = []
+ if isinstance(propclass, hyperdb.Multilink): value = []
else: value = ''
- if propclass.isStringType:
+ if isinstance(propclass, hyperdb.String):
if value is None: value = ''
else: value = str(value)
- elif propclass.isDateType:
+ elif isinstance(propclass, hyperdb.Date):
value = str(value)
- elif propclass.isIntervalType:
+ elif isinstance(propclass, hyperdb.Interval):
value = str(value)
- elif propclass.isLinkType:
+ elif isinstance(propclass, hyperdb.Link):
linkcl = self.db.classes[propclass.classname]
k = linkcl.labelprop()
if value: value = str(linkcl.get(value, k))
else: value = '[unselected]'
- elif propclass.isMultilinkType:
+ elif isinstance(propclass, hyperdb.Multilink):
linkcl = self.db.classes[propclass.classname]
k = linkcl.labelprop()
value = ', '.join([linkcl.get(i, k) for i in value])
to be edited
'''
def __call__(self, property, size=None, height=None, showid=0):
- if not self.nodeid and self.form is None:
+ if not self.nodeid and self.form is None and self.filterspec is None:
return '[Field: not called from item]'
propclass = self.properties[property]
if self.nodeid:
- value = self.cl.get(self.nodeid, property)
+ value = self.cl.get(self.nodeid, property, None)
+ # TODO: remove this from the code ... it's only here for
+ # handling schema changes, and they should be handled outside
+ # of this code...
+ if isinstance(propclass, hyperdb.Multilink) and value is None:
+ value = []
+ elif self.filterspec is not None:
+ if isinstance(propclass, hyperdb.Multilink):
+ value = self.filterspec.get(property, [])
+ else:
+ value = self.filterspec.get(property, '')
else:
# TODO: pull the value from the form
- if propclass.isMultilinkType: value = []
+ if isinstance(propclass, hyperdb.Multilink): value = []
else: value = ''
- if (propclass.isStringType or propclass.isDateType or
- propclass.isIntervalType):
+ if (isinstance(propclass, hyperdb.String) or
+ isinstance(propclass, hyperdb.Date) or
+ isinstance(propclass, hyperdb.Interval)):
size = size or 30
if value is None:
value = ''
value = cgi.escape(value)
value = '"'.join(value.split('"'))
s = '<input name="%s" value="%s" size="%s">'%(property, value, size)
- elif propclass.isLinkType:
+ elif isinstance(propclass, hyperdb.Link):
linkcl = self.db.classes[propclass.classname]
l = ['<select name="%s">'%property]
k = linkcl.labelprop()
l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
l.append('</select>')
s = '\n'.join(l)
- elif propclass.isMultilinkType:
+ elif isinstance(propclass, hyperdb.Multilink):
linkcl = self.db.classes[propclass.classname]
list = linkcl.list()
height = height or min(len(list), 7)
value = self.cl.get(self.nodeid, property)
else:
# TODO: pull the value from the form
- if propclass.isMultilinkType: value = []
+ if isinstance(propclass, hyperdb.Multilink): value = []
else: value = None
- if propclass.isLinkType:
+ if isinstance(propclass, hyperdb.Link):
linkcl = self.db.classes[propclass.classname]
l = ['<select name="%s">'%property]
k = linkcl.labelprop()
l.append('<option %svalue="%s">%s</option>'%(s, optionid, option))
l.append('</select>')
return '\n'.join(l)
- if propclass.isMultilinkType:
+ if isinstance(propclass, hyperdb.Multilink):
linkcl = self.db.classes[propclass.classname]
list = linkcl.list()
height = height or min(len(list), 7)
if self.nodeid:
value = self.cl.get(self.nodeid, property)
else:
- if propclass.isMultilinkType: value = []
+ if isinstance(propclass, hyperdb.Multilink): value = []
else: value = ''
- if propclass.isLinkType:
+ if isinstance(propclass, hyperdb.Link):
if value is None:
return '[not assigned]'
linkcl = self.db.classes[propclass.classname]
k = linkcl.labelprop()
linkvalue = linkcl.get(value, k)
return '<a href="%s%s">%s</a>'%(linkcl, value, linkvalue)
- if propclass.isMultilinkType:
+ if isinstance(propclass, hyperdb.Multilink):
linkcl = self.db.classes[propclass.classname]
k = linkcl.labelprop()
l = []
return '[Count: not called from item]'
propclass = self.properties[property]
value = self.cl.get(self.nodeid, property)
- if propclass.isMultilinkType:
+ if isinstance(propclass, hyperdb.Multilink):
return str(len(value))
return '[Count: not a Multilink]'
if not self.nodeid and self.form is None:
return '[Reldate: not called from item]'
propclass = self.properties[property]
- if not propclass.isDateType:
+ if isinstance(not propclass, hyperdb.Date):
return '[Reldate: not a Date]'
if self.nodeid:
value = self.cl.get(self.nodeid, property)
return '[Download: not called from item]'
propclass = self.properties[property]
value = self.cl.get(self.nodeid, property)
- if propclass.isLinkType:
+ if isinstance(propclass, hyperdb.Link):
linkcl = self.db.classes[propclass.classname]
linkvalue = linkcl.get(value, k)
return '<a href="%s%s">%s</a>'%(linkcl, value, linkvalue)
- if propclass.isMultilinkType:
+ if isinstance(propclass, hyperdb.Multilink):
linkcl = self.db.classes[propclass.classname]
l = []
for value in value:
propclass = self.properties[property]
if self.nodeid:
value = self.cl.get(self.nodeid, property)
+ elif self.filterspec is not None:
+ value = self.filterspec.get(property, [])
else:
value = []
- if propclass.isLinkType or propclass.isMultilinkType:
+ if (isinstance(propclass, hyperdb.Link) or
+ isinstance(propclass, hyperdb.Multilink)):
linkcl = self.db.classes[propclass.classname]
l = []
k = linkcl.labelprop()
for optionid in linkcl.list():
option = linkcl.get(optionid, k)
- if optionid in value:
+ if optionid in value or option in value:
checked = 'checked'
else:
checked = ''
l.append('%s:<input type="checkbox" %s name="%s" value="%s">'%(
- option, checked, propclass.classname, option))
+ option, checked, property, option))
return '\n'.join(l)
return '[Checklist: not a link]'
'''
def __call__(self, property, **args):
propclass = self.properties[property]
- if not propclass.isMultilinkType:
+ if isinstance(not propclass, hyperdb.Multilink):
return '[List: not a Multilink]'
fp = StringIO.StringIO()
args['show_display_form'] = 0
''' list the history of the item
'''
def __call__(self, **args):
+ if self.nodeid is None:
+ return "[History: node doesn't exist]"
+
l = ['<table width=100% border=0 cellspacing=0 cellpadding=2>',
'<tr class="list-header">',
'<td><span class="list-item"><strong>Date</strong></span></td>',
r'((<property\s+name="(?P<name>[^>]+)">(?P<text>.+?)</property>)|'
r'(?P<display><display\s+call="(?P<command>[^"]+)">))', re.I|re.S)):
return replace.sub(self, text)
-
+
def __call__(self, m, filter=None, columns=None, sort=None, group=None):
if m.group('name'):
if m.group('name') in self.props:
for name in sort:
dir = name[0]
if dir == '-':
- dir = ''
- else:
name = name[1:]
+ else:
+ dir = ''
if sort_name == name:
- if dir == '':
- s_dir = '-'
- elif dir == '-':
+ if dir == '-':
s_dir = ''
+ else:
+ s_dir = '-'
else:
m.append(dir+urllib.quote(name))
m.insert(0, s_dir+urllib.quote(sort_name))
columns=[], sort=[], group=[], show_display_form=1, nodeids=None,
col_re=re.compile(r'<property\s+name="([^>]+)">')):
globals = {
- 'plain': Plain(db, templates, classname, form={}),
- 'field': Field(db, templates, classname, form={}),
- 'menu': Menu(db, templates, classname, form={}),
- 'link': Link(db, templates, classname, form={}),
- 'count': Count(db, templates, classname, form={}),
- 'reldate': Reldate(db, templates, classname, form={}),
- 'download': Download(db, templates, classname, form={}),
- 'checklist': Checklist(db, templates, classname, form={}),
- 'list': List(db, templates, classname, form={}),
- 'history': History(db, templates, classname, form={}),
- 'submit': Submit(db, templates, classname, form={}),
- 'note': Note(db, templates, classname, form={})
+ 'plain': Plain(db, templates, classname, filterspec=filterspec),
+ 'field': Field(db, templates, classname, filterspec=filterspec),
+ 'menu': Menu(db, templates, classname, filterspec=filterspec),
+ 'link': Link(db, templates, classname, filterspec=filterspec),
+ 'count': Count(db, templates, classname, filterspec=filterspec),
+ 'reldate': Reldate(db, templates, classname, filterspec=filterspec),
+ 'download': Download(db, templates, classname, filterspec=filterspec),
+ 'checklist': Checklist(db, templates, classname, filterspec=filterspec),
+ 'list': List(db, templates, classname, filterspec=filterspec),
+ 'history': History(db, templates, classname, filterspec=filterspec),
+ 'submit': Submit(db, templates, classname, filterspec=filterspec),
+ 'note': Note(db, templates, classname, filterspec=filterspec)
}
cl = db.classes[classname]
properties = cl.getprops()
w = client.write
+ w('<form>')
try:
template = open(os.path.join(templates, classname+'.filter')).read()
all_filters = []
if template and filter:
# display the filter section
- w('<form>')
w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
w('<tr class="location-bar">')
w(' <th align="left" colspan="2">Filter specification...</th>')
w('</tr>')
replace = IndexTemplateReplace(globals, locals(), filter)
w(replace.go(template))
- if columns:
- w('<input type="hidden" name=":columns" value="%s">'%','.join(columns))
- if filter:
- w('<input type="hidden" name=":filter" value="%s">'%','.join(filter))
- if sort:
- w('<input type="hidden" name=":sort" value="%s">'%','.join(sort))
- if group:
- w('<input type="hidden" name=":group" value="%s">'%','.join(group))
- for k, v in filterspec.items():
- if type(v) == type([]): v = ','.join(v)
- w('<input type="hidden" name="%s" value="%s">'%(k, v))
w('<tr class="location-bar"><td width="1%%"> </td>')
w('<td><input type="submit" value="Redisplay"></td></tr>')
w('</table>')
- w('</form>')
+
+ # If the filters aren't being displayed, then hide their current
+ # value in the form
+ if not filter:
+ for k, v in filterspec.items():
+ if type(v) == type([]): v = ','.join(v)
+ w('<input type="hidden" name="%s" value="%s">'%(k, v))
+
+ # make sure that the sorting doesn't get lost either
+ if sort:
+ w('<input type="hidden" name=":sort" value="%s">'%','.join(sort))
# XXX deviate from spec here ...
# load the index section template and figure the default columns from it
columns = l
# now display the index section
- w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
- w('<tr class="list-header">')
+ w('<table width=100% border=0 cellspacing=0 cellpadding=2>\n')
+ w('<tr class="list-header">\n')
for name in columns:
cname = name.capitalize()
if show_display_form:
anchor = "%s?%s"%(classname, sortby(name, columns, filter,
sort, group, filterspec))
- w('<td><span class="list-item"><a href="%s">%s</a></span></td>'%(
+ w('<td><span class="list-item"><a href="%s">%s</a></span></td>\n'%(
anchor, cname))
else:
- w('<td><span class="list-item">%s</span></td>'%cname)
- w('</tr>')
+ w('<td><span class="list-item">%s</span></td>\n'%cname)
+ w('</tr>\n')
# this stuff is used for group headings - optimise the group names
old_group = None
l = []
for name in group_names:
prop = properties[name]
- if prop.isLinkType:
+ if isinstance(prop, hyperdb.Link):
group_cl = db.classes[prop.classname]
key = group_cl.getkey()
value = cl.get(nodeid, name)
l.append('[unselected %s]'%prop.classname)
else:
l.append(group_cl.get(cl.get(nodeid, name), key))
- elif prop.isMultilinkType:
+ elif isinstance(prop, hyperdb.Multilink):
group_cl = db.classes[prop.classname]
key = group_cl.getkey()
for value in cl.get(nodeid, name):
return
# now add in the filter/columns/group/etc config table form
- w('<p><form>')
- w('<table width=100% border=0 cellspacing=0 cellpadding=2>')
- for k,v in filterspec.items():
- if type(v) == type([]): v = ','.join(v)
- w('<input type="hidden" name="%s" value="%s">'%(k, v))
- if sort:
- w('<input type="hidden" name=":sort" value="%s">'%','.join(sort))
+ w('<p>')
+ w('<table width=100% border=0 cellspacing=0 cellpadding=2>\n')
names = []
for name in cl.getprops().keys():
if name in all_filters or name in all_columns:
names.append(name)
w('<tr class="location-bar">')
- w('<th align="left" colspan=%s>View customisation...</th></tr>'%
+ w('<th align="left" colspan=%s>View customisation...</th></tr>\n'%
(len(names)+1))
w('<tr class="location-bar"><th> </th>')
for name in names:
w('<th>%s</th>'%name.capitalize())
- w('</tr>')
+ w('</tr>\n')
# filter
if all_filters:
- w('<tr><th width="1%" align=right class="location-bar">Filters</th>')
+ w('<tr><th width="1%" align=right class="location-bar">Filters</th>\n')
for name in names:
if name not in all_filters:
w('<td> </td>')
continue
if name in filter: checked=' checked'
else: checked=''
- w('<td align=middle>')
- w('<input type="checkbox" name=":filter" value="%s" %s></td>'%(name,
- checked))
- w('</tr>')
+ w('<td align=middle>\n')
+ w(' <input type="checkbox" name=":filter" value="%s" %s></td>\n'%(
+ name, checked))
+ w('</tr>\n')
# columns
if all_columns:
- w('<tr><th width="1%" align=right class="location-bar">Columns</th>')
+ w('<tr><th width="1%" align=right class="location-bar">Columns</th>\n')
for name in names:
if name not in all_columns:
w('<td> </td>')
continue
if name in columns: checked=' checked'
else: checked=''
- w('<td align=middle>')
- w('<input type="checkbox" name=":columns" value="%s" %s></td>'%(
+ w('<td align=middle>\n')
+ w(' <input type="checkbox" name=":columns" value="%s" %s></td>\n'%(
name, checked))
- w('</tr>')
+ w('</tr>\n')
# group
- w('<tr><th width="1%" align=right class="location-bar">Grouping</th>')
+ w('<tr><th width="1%" align=right class="location-bar">Grouping</th>\n')
for name in names:
prop = properties[name]
if name not in all_columns:
continue
if name in group: checked=' checked'
else: checked=''
- w('<td align=middle>')
- w('<input type="checkbox" name=":group" value="%s" %s></td>'%(
+ w('<td align=middle>\n')
+ w(' <input type="checkbox" name=":group" value="%s" %s></td>\n'%(
name, checked))
- w('</tr>')
+ w('</tr>\n')
w('<tr class="location-bar"><td width="1%"> </td>')
w('<td colspan="%s">'%len(names))
- w('<input type="submit" value="Redisplay"></td></tr>')
- w('</table>')
- w('</form>')
+ w('<input type="submit" value="Redisplay"></td></tr>\n')
+ w('</table>\n')
+ w('</form>\n')
#
s = open(os.path.join(templates, classname+'.newitem')).read()
except:
s = open(os.path.join(templates, classname+'.item')).read()
- w('<form action="new%s">'%classname)
+ w('<form action="new%s" method="POST" enctype="multipart/form-data">'%classname)
+ for key in form.keys():
+ if key[0] == ':':
+ value = form[key].value
+ if type(value) != type([]): value = [value]
+ for value in value:
+ w('<input type="hidden" name="%s" value="%s">'%(key, value))
replace = ItemTemplateReplace(globals, locals(), None, None)
w(replace.go(s))
w('</form>')
#
# $Log: not supported by cvs2svn $
+# Revision 1.20 2001/08/15 23:43:18 richard
+# Fixed some isFooTypes that I missed.
+# Refactored some code in the CGI code.
+#
+# Revision 1.19 2001/08/12 06:32:36 richard
+# using isinstance(blah, Foo) now instead of isFooType
+#
+# Revision 1.18 2001/08/07 00:24:42 richard
+# stupid typo
+#
+# Revision 1.17 2001/08/07 00:15:51 richard
+# Added the copyright/license notice to (nearly) all files at request of
+# Bizar Software.
+#
+# Revision 1.16 2001/08/01 03:52:23 richard
+# Checklist was using wrong name.
+#
+# Revision 1.15 2001/07/30 08:12:17 richard
+# Added time logging and file uploading to the templates.
+#
+# Revision 1.14 2001/07/30 06:17:45 richard
+# Features:
+# . Added ability for cgi newblah forms to indicate that the new node
+# should be linked somewhere.
+# Fixed:
+# . Fixed the agument handling for the roundup-admin find command.
+# . Fixed handling of summary when no note supplied for newblah. Again.
+# . Fixed detection of no form in htmltemplate Field display.
+#
+# Revision 1.13 2001/07/30 02:37:53 richard
+# Temporary measure until we have decent schema migration.
+#
+# Revision 1.12 2001/07/30 01:24:33 richard
+# Handles new node display now.
+#
+# Revision 1.11 2001/07/29 09:31:35 richard
+# oops
+#
+# Revision 1.10 2001/07/29 09:28:23 richard
+# Fixed sorting by clicking on column headings.
+#
+# Revision 1.9 2001/07/29 08:27:40 richard
+# Fixed handling of passed-in values in form elements (ie. during a
+# drill-down)
+#
+# Revision 1.8 2001/07/29 07:01:39 richard
+# Added vim command to all source so that we don't get no steenkin' tabs :)
+#
# Revision 1.7 2001/07/29 05:36:14 richard
# Cleanup of the link label generation.
#