X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup%2Fhtmltemplate.py;h=2f6557a1fdb0abba2b3e4d660048c9cfdc2e923b;hb=f04ca7e8c6067e05296508ed2b7c302425ffbcc8;hp=c7075b3150dd3360a848028afdd81c348cc92fa0;hpb=53e22cb4badc961ccf3b5211d6212bc6a81419fb;p=roundup.git
diff --git a/roundup/htmltemplate.py b/roundup/htmltemplate.py
index c7075b3..2f6557a 100644
--- a/roundup/htmltemplate.py
+++ b/roundup/htmltemplate.py
@@ -1,115 +1,211 @@
-# $Id: htmltemplate.py,v 1.5 2001-07-28 08:17:09 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.89 2002-05-15 06:34:47 richard Exp $
-import os, re, StringIO, urllib, cgi, errno
+__doc__ = """
+Template engine.
+"""
-import hyperdb, date
+import os, re, StringIO, urllib, cgi, errno, types, urllib
-class Base:
- def __init__(self, db, templates, classname, nodeid=None, form=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.cl = self.db.classes[self.classname]
- self.properties = self.cl.getprops()
+import hyperdb, date
+from i18n import _
-class Plain(Base):
- ''' display a String property directly;
+# This imports the StructureText functionality for the do_stext function
+# get it from http://dev.zope.org/Members/jim/StructuredTextWiki/NGReleases
+try:
+ from StructuredText.StructuredText import HTML as StructuredText
+except ImportError:
+ StructuredText = None
- display a Date property in a specified time zone with an option to
- omit the time from the date stamp;
+class MissingTemplateError(ValueError):
+ '''Error raised when a template file is missing
+ '''
+ pass
- for a Link or Multilink property, display the key strings of the
- linked nodes (or the ids if the linked class has no key property)
+class TemplateFunctions:
+ '''Defines the templating functions that are used in the HTML templates
+ of the roundup web interface.
'''
- def __call__(self, property):
+ def __init__(self):
+ self.form = None
+ self.nodeid = None
+ self.filterspec = None
+ self.globals = {}
+ for key in TemplateFunctions.__dict__.keys():
+ if key[:3] == 'do_':
+ self.globals[key[3:]] = getattr(self, key)
+
+ # These are added by the subclass where appropriate
+ self.client = None
+ self.instance = None
+ self.templates = None
+ self.classname = None
+ self.db = None
+ self.cl = None
+ self.properties = None
+
+ def do_plain(self, property, escape=0):
+ ''' display a String property directly;
+
+ display a Date property in a specified time zone with an option to
+ omit the time from the date stamp;
+
+ for a Link or Multilink property, display the key strings of the
+ linked nodes (or the ids if the linked class has no key property)
+ '''
if not self.nodeid and self.form is None:
- return '[Field: not called from item]'
+ return _('[Field: not called from item]')
propclass = self.properties[property]
if self.nodeid:
- value = self.cl.get(self.nodeid, property)
+ # make sure the property is a valid one
+ # TODO: this tests, but we should handle the exception
+ dummy = self.cl.getprops()[property]
+
+ # get the value for this property
+ try:
+ value = self.cl.get(self.nodeid, property)
+ except KeyError:
+ # a KeyError here means that the node doesn't have a value
+ # for the specified property
+ if isinstance(propclass, hyperdb.Multilink): value = []
+ else: value = ''
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.Password):
+ if value is None: value = ''
+ else: value = _('*encrypted*')
+ elif isinstance(propclass, hyperdb.Date):
+ # this gives "2002-01-17.06:54:39", maybe replace the "." by a " ".
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.getkey()
- # if the linked-to class doesn't have a key property, then try
- # 'name', then 'title' and then just use a random one.
- if not k:
- linkprops = linkcl.getprops()
- if linkprops.has_key('name'):
- k = 'name'
- elif linkprops.has_key('title'):
- k = 'title'
- else:
- k = linkprops.keys()[0]
- if value: value = str(linkcl.get(value, k))
- else: value = '[unselected]'
- elif propclass.isMultilinkType:
+ k = linkcl.labelprop()
+ if value:
+ value = linkcl.get(value, k)
+ else:
+ value = _('[unselected]')
+ elif isinstance(propclass, hyperdb.Multilink):
linkcl = self.db.classes[propclass.classname]
- k = linkcl.getkey()
- # if the linked-to class doesn't have a key property, then try
- # 'name', then 'title' and then just use a random one.
- if not k:
- linkprops = linkcl.getprops()
- if linkprops.has_key('name'):
- k = 'name'
- elif linkprops.has_key('title'):
- k = 'title'
- else:
- k = linkprops.keys()[0]
- value = ', '.join([linkcl.get(i, k) for i in value])
+ k = linkcl.labelprop()
+ labels = []
+ for v in value:
+ labels.append(linkcl.get(v, k))
+ value = ', '.join(labels)
else:
- s = 'Plain: bad propclass "%s"'%propclass
+ value = _('Plain: bad propclass "%(propclass)s"')%locals()
+ if escape:
+ value = cgi.escape(value)
return value
-class Field(Base):
- ''' display a property like the plain displayer, but in a text field
- to be edited
- '''
- def __call__(self, property, size=None, height=None, showid=0):
- if not self.nodeid and self.form is None:
- return '[Field: not called from item]'
+ def do_stext(self, property, escape=0):
+ '''Render as structured text using the StructuredText module
+ (see above for details)
+ '''
+ s = self.do_plain(property, escape=escape)
+ if not StructuredText:
+ return s
+ return StructuredText(s,level=1,header=0)
+
+ def determine_value(self, property):
+ '''determine the value of a property using the node, form or
+ filterspec
+ '''
propclass = self.properties[property]
if self.nodeid:
- value = self.cl.get(self.nodeid, property)
+ value = self.cl.get(self.nodeid, property, None)
+ if isinstance(propclass, hyperdb.Multilink) and value is None:
+ return []
+ return value
+ elif self.filterspec is not None:
+ if isinstance(propclass, hyperdb.Multilink):
+ return self.filterspec.get(property, [])
+ else:
+ return self.filterspec.get(property, '')
+ # TODO: pull the value from the form
+ if isinstance(propclass, hyperdb.Multilink):
+ return []
else:
- # TODO: pull the value from the form
- if propclass.isMultilinkType: value = []
- else: value = ''
- if (propclass.isStringType or propclass.isDateType or
- propclass.isIntervalType):
- size = size or 30
+ return ''
+
+ def make_sort_function(self, classname):
+ '''Make a sort function for a given class
+ '''
+ linkcl = self.db.classes[classname]
+ if linkcl.getprops().has_key('order'):
+ sort_on = 'order'
+ else:
+ sort_on = linkcl.labelprop()
+ def sortfunc(a, b, linkcl=linkcl, sort_on=sort_on):
+ return cmp(linkcl.get(a, sort_on), linkcl.get(b, sort_on))
+ return sortfunc
+
+ def do_field(self, property, size=None, showid=0):
+ ''' display a property like the plain displayer, but in a text field
+ to be edited
+
+ Note: if you would prefer an option list style display for
+ link or multilink editing, use menu().
+ '''
+ if not self.nodeid and self.form is None and self.filterspec is None:
+ return _('[Field: not called from item]')
+
+ if size is None:
+ size = 30
+
+ propclass = self.properties[property]
+
+ # get the value
+ value = self.determine_value(property)
+
+ # now display
+ if (isinstance(propclass, hyperdb.String) or
+ isinstance(propclass, hyperdb.Date) or
+ isinstance(propclass, hyperdb.Interval)):
if value is None:
value = ''
else:
- value = cgi.escape(value)
+ value = cgi.escape(str(value))
value = '"'.join(value.split('"'))
s = ''%(property, value, size)
- elif propclass.isLinkType:
+ elif isinstance(propclass, hyperdb.Password):
+ s = ''%(property, size)
+ elif isinstance(propclass, hyperdb.Link):
+ sortfunc = self.make_sort_function(propclass.classname)
linkcl = self.db.classes[propclass.classname]
+ options = linkcl.list()
+ options.sort(sortfunc)
+ # TODO: make this a field display, not a menu one!
l = ['')
s = '\n'.join(l)
- elif propclass.isMultilinkType:
+ elif isinstance(propclass, hyperdb.Multilink):
+ sortfunc = self.make_sort_function(propclass.classname)
linkcl = self.db.classes[propclass.classname]
- list = linkcl.list()
- height = height or min(len(list), 7)
+ if value:
+ value.sort(sortfunc)
+ # map the id to the label property
+ if not showid:
+ k = linkcl.labelprop()
+ value = [linkcl.get(v, k) for v in value]
+ value = cgi.escape(','.join(value))
+ s = ''%(property, size, value)
+ else:
+ s = _('Plain: bad propclass "%(propclass)s"')%locals()
+ return s
+
+ def do_multiline(self, property, rows=5, cols=40):
+ ''' display a string property in a multiline text edit field
+ '''
+ if not self.nodeid and self.form is None and self.filterspec is None:
+ return _('[Multiline: not called from item]')
+
+ propclass = self.properties[property]
+
+ # make sure this is a link property
+ if not isinstance(propclass, hyperdb.String):
+ return _('[Multiline: not a string]')
+
+ # get the value
+ value = self.determine_value(property)
+ if value is None:
+ value = ''
+
+ # display
+ return ''%(
+ property, rows, cols, value)
+
+ def do_menu(self, property, size=None, height=None, showid=0):
+ ''' for a Link property, display a menu of the available choices
+ '''
+ if not self.nodeid and self.form is None and self.filterspec is None:
+ return _('[Field: not called from item]')
+
+ propclass = self.properties[property]
+
+ # make sure this is a link property
+ if not (isinstance(propclass, hyperdb.Link) or
+ isinstance(propclass, hyperdb.Multilink)):
+ return _('[Menu: not a link]')
+
+ # sort function
+ sortfunc = self.make_sort_function(propclass.classname)
+
+ # get the value
+ value = self.determine_value(property)
+
+ # display
+ if isinstance(propclass, hyperdb.Multilink):
+ linkcl = self.db.classes[propclass.classname]
+ options = linkcl.list()
+ options.sort(sortfunc)
+ height = height or min(len(options), 7)
l = ['')
- s = '\n'.join(l)
- else:
- s = 'Plain: bad propclass "%s"'%propclass
- return s
-
-class Menu(Base):
- ''' for a Link property, display a menu of the available choices
- '''
- def __call__(self, property, size=None, height=None, showid=0):
- propclass = self.properties[property]
- if self.nodeid:
- value = self.cl.get(self.nodeid, property)
- else:
- # TODO: pull the value from the form
- if propclass.isMultilinkType: value = []
- else: value = None
- if propclass.isLinkType:
- linkcl = self.db.classes[propclass.classname]
- l = ['')
return '\n'.join(l)
- if propclass.isMultilinkType:
+ if isinstance(propclass, hyperdb.Link):
+ # force the value to be a single choice
+ if type(value) is types.ListType:
+ value = value[0]
linkcl = self.db.classes[propclass.classname]
- list = linkcl.list()
- height = height or min(len(list), 7)
- l = ['