Initial Hawkeye work. This contains a new template system for creating HTML, some basic admin ability, and serves as the basis for further work.
You may need to bongo-config install again, if upgrading.
This commit is contained in:
@@ -11,6 +11,7 @@ pkgpythonexternalemaildir = $(pythondir)/bongo/external/email
|
||||
pkgpythonexternalemailmimedir = $(pythondir)/bongo/external/email/mime
|
||||
pkgpythonexternaldateutilzoneinfodir = $(pythondir)/bongo/external/dateutil/zoneinfo
|
||||
pkgpythonexternalsimplejsondir = $(pythondir)/bongo/external/simplejson
|
||||
pkgpythonexternalsimpletaldir = $(pythondir)/bongo/external/simpletal
|
||||
pkgpythonexternalvobjectdir = $(pythondir)/bongo/external/vobject
|
||||
pkgpythonnmapdir = $(pythondir)/bongo/nmap
|
||||
pkgpythonstoredir = $(pythondir)/bongo/store
|
||||
@@ -19,10 +20,11 @@ pkgpython_PYTHON := \
|
||||
src/libs/python/bongo/__init__.py \
|
||||
src/libs/python/bongo/CalCmd.py \
|
||||
src/libs/python/bongo/cmdparse.py \
|
||||
src/libs/python/bongo/Console.py \
|
||||
src/libs/python/bongo/Contact.py \
|
||||
src/libs/python/bongo/Console.py \
|
||||
src/libs/python/bongo/Contact.py \
|
||||
src/libs/python/bongo/BongoError.py \
|
||||
src/libs/python/bongo/MDB.py \
|
||||
src/libs/python/bongo/Template.py \
|
||||
src/libs/python/bongo/Privs.py \
|
||||
src/libs/python/bongo/StreamIO.py \
|
||||
src/libs/python/bongo/table.py
|
||||
@@ -110,6 +112,17 @@ pkgpythonexternalsimplejson_PYTHON := \
|
||||
src/libs/python/bongo/external/simplejson/jsonfilter.py \
|
||||
src/libs/python/bongo/external/simplejson/scanner.py
|
||||
|
||||
pkgpythonexternalsimpletal_PYTHON := \
|
||||
src/libs/python/bongo/external/simpletal/DummyLogger.py \
|
||||
src/libs/python/bongo/external/simpletal/__init__.py \
|
||||
src/libs/python/bongo/external/simpletal/simpleElementTree.py \
|
||||
src/libs/python/bongo/external/simpletal/simpleTAL.py \
|
||||
src/libs/python/bongo/external/simpletal/FixedHTMLParser.py \
|
||||
src/libs/python/bongo/external/simpletal/sgmlentitynames.py \
|
||||
src/libs/python/bongo/external/simpletal/simpleTALES.py \
|
||||
src/libs/python/bongo/external/simpletal/simpleTALUtils.py
|
||||
|
||||
|
||||
pkgpythonexternalvobject_PYTHON := \
|
||||
src/libs/python/bongo/external/vobject/__init__.py \
|
||||
src/libs/python/bongo/external/vobject/base.py \
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import os, sys, copy
|
||||
from bongo.external.simpletal import simpleTAL, simpleTALES
|
||||
|
||||
class Template:
|
||||
def __init__(self, depth=0):
|
||||
self._context = simpleTALES.Context()
|
||||
self._depth = depth
|
||||
self._templatefile = None
|
||||
self._preprocessor = TemplatePreprocessor()
|
||||
|
||||
def Run(self, req):
|
||||
if self._templatefile == None:
|
||||
raise RuntimeError
|
||||
|
||||
self._preprocessor.setTemplateFile(self._templatefile)
|
||||
template = simpleTAL.compileHTMLTemplate(self._preprocessor)
|
||||
template.expand(self._context, req)
|
||||
|
||||
def setVariable(self, name, obj):
|
||||
self._context.addGlobal(name, obj)
|
||||
self._preprocessor[name] = obj
|
||||
|
||||
def setTemplateFile(self, filename):
|
||||
self._templatefile = filename
|
||||
|
||||
def setTemplatePath(self, path):
|
||||
self._templatepath = path
|
||||
self._preprocessor.setTemplatePath(path)
|
||||
|
||||
def setTemplateUriRoot(self, root):
|
||||
self._templateuriroot = root
|
||||
self._preprocessor.setUriRoot(root)
|
||||
|
||||
class TemplatePreprocessor:
|
||||
def __init__(self, dict={}):
|
||||
self.dict = dict
|
||||
|
||||
def read(self):
|
||||
return "%s" % self
|
||||
|
||||
def setTemplateFile(self, filename):
|
||||
content = self._filecontent(filename)
|
||||
if content != "":
|
||||
self._template = content
|
||||
else:
|
||||
self._template = "<html><body><h1>Error</h1><p>No template %s</p></body></html>" % filename
|
||||
|
||||
def __str__(self):
|
||||
# rely on string substitutions
|
||||
return self._template % self
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._process(key.split("|"))
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.dict[key] = value
|
||||
|
||||
def _process(self, args):
|
||||
arg = args.pop(0)
|
||||
if len(args) == 0:
|
||||
if arg in self.dict:
|
||||
return self.dict[arg]
|
||||
elif hasattr(self, "call_" + arg) and callable(getattr(self, "call_" + arg)):
|
||||
return getattr(self, "call_" + arg)()
|
||||
else:
|
||||
return "Bad attribute %s" % arg
|
||||
else:
|
||||
return getattr(self, "call_" + arg)(args)
|
||||
|
||||
def _filecontent(self, filename):
|
||||
content = ""
|
||||
try:
|
||||
f = open (filename, "r")
|
||||
content = f.read()
|
||||
f.close()
|
||||
except:
|
||||
print "Couldn't open file %s" % filename
|
||||
return content
|
||||
|
||||
def setTemplatePath(self, path):
|
||||
self._templatepath = path
|
||||
|
||||
def setUriRoot(self, root):
|
||||
self._uriroot = root
|
||||
|
||||
# template callable functions
|
||||
def call_url(self, args):
|
||||
# Pad out URLs with our web root, so the app can be moved in terms of
|
||||
# URL and the links all still work
|
||||
return self._uriroot + args[0]
|
||||
|
||||
def call_include(self, args):
|
||||
# Include a copy of another template here. This is also processed.
|
||||
template = self._templatepath + '/' + args[0]
|
||||
pp = copy.copy(self)
|
||||
pp.setTemplateFile(template)
|
||||
return pp.read()
|
||||
@@ -0,0 +1,66 @@
|
||||
""" simpleTALES Implementation
|
||||
|
||||
Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
If you make any bug fixes or feature enhancements please let me know!
|
||||
|
||||
Dummy logging module, used when logging (http://www.red-dove.com/python_logging.html)
|
||||
is not installed.
|
||||
"""
|
||||
|
||||
class DummyLogger:
|
||||
def debug (self, *args):
|
||||
pass
|
||||
|
||||
def info (self, *args):
|
||||
pass
|
||||
|
||||
def warn (self, *args):
|
||||
pass
|
||||
|
||||
def error (self, *args):
|
||||
pass
|
||||
|
||||
def critical (self, *args):
|
||||
pass
|
||||
|
||||
def getLogger (*params):
|
||||
return DummyLogger()
|
||||
|
||||
def debug (*args):
|
||||
pass
|
||||
|
||||
def info (*args):
|
||||
pass
|
||||
|
||||
def warn (*args):
|
||||
pass
|
||||
|
||||
def error (*args):
|
||||
pass
|
||||
|
||||
def critical (*args):
|
||||
pass
|
||||
@@ -0,0 +1,45 @@
|
||||
""" Fixed up HTMLParser
|
||||
|
||||
Copyright (c) 2005 Colin Stewart (http://www.owlfish.com/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
If you make any bug fixes or feature enhancements please let me know!
|
||||
|
||||
|
||||
The classes in this module implement the TAL language, expanding
|
||||
both XML and HTML templates.
|
||||
|
||||
Module Dependencies: logging, simpleTALES, simpleTALTemplates
|
||||
"""
|
||||
|
||||
import bongo.external.simpletal
|
||||
import HTMLParser
|
||||
|
||||
|
||||
class HTMLParser (HTMLParser.HTMLParser):
|
||||
def unescape(self, s):
|
||||
# Just return the data - we don't partially unescaped data!
|
||||
return s
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
__version__ = "4.1"
|
||||
@@ -0,0 +1,284 @@
|
||||
""" simpleTAL Implementation
|
||||
|
||||
Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
If you make any bug fixes or feature enhancements please let me know!
|
||||
|
||||
A dictionary of HTML entity names to their decimal value.
|
||||
"""
|
||||
|
||||
htmlNameToUnicodeNumber = {"nbsp": 160
|
||||
,"iexcl": 161
|
||||
,"cent": 162
|
||||
,"pound": 163
|
||||
,"curren": 164
|
||||
,"yen": 165
|
||||
,"brvbar": 166
|
||||
,"sect": 167
|
||||
,"uml": 168
|
||||
,"copy": 169
|
||||
,"ordf": 170
|
||||
,"laquo": 171
|
||||
,"not": 172
|
||||
,"shy": 173
|
||||
,"reg": 174
|
||||
,"macr": 175
|
||||
,"deg": 176
|
||||
,"plusmn": 177
|
||||
,"sup2": 178
|
||||
,"sup3": 179
|
||||
,"acute": 180
|
||||
,"micro": 181
|
||||
,"para": 182
|
||||
,"middot": 183
|
||||
,"cedil": 184
|
||||
,"sup1": 185
|
||||
,"ordm": 186
|
||||
,"raquo": 187
|
||||
,"frac14": 188
|
||||
,"frac12": 189
|
||||
,"frac34": 190
|
||||
,"iquest": 191
|
||||
,"Agrave": 192
|
||||
,"Aacute": 193
|
||||
,"Acirc": 194
|
||||
,"Atilde": 195
|
||||
,"Auml": 196
|
||||
,"Aring": 197
|
||||
,"AElig": 198
|
||||
,"Ccedil": 199
|
||||
,"Egrave": 200
|
||||
,"Eacute": 201
|
||||
,"Ecirc": 202
|
||||
,"Euml": 203
|
||||
,"Igrave": 204
|
||||
,"Iacute": 205
|
||||
,"Icirc": 206
|
||||
,"Iuml": 207
|
||||
,"ETH": 208
|
||||
,"Ntilde": 209
|
||||
,"Ograve": 210
|
||||
,"Oacute": 211
|
||||
,"Ocirc": 212
|
||||
,"Otilde": 213
|
||||
,"Ouml": 214
|
||||
,"times": 215
|
||||
,"Oslash": 216
|
||||
,"Ugrave": 217
|
||||
,"Uacute": 218
|
||||
,"Ucirc": 219
|
||||
,"Uuml": 220
|
||||
,"Yacute": 221
|
||||
,"THORN": 222
|
||||
,"szlig": 223
|
||||
,"agrave": 224
|
||||
,"aacute": 225
|
||||
,"acirc": 226
|
||||
,"atilde": 227
|
||||
,"auml": 228
|
||||
,"aring": 229
|
||||
,"aelig": 230
|
||||
,"ccedil": 231
|
||||
,"egrave": 232
|
||||
,"eacute": 233
|
||||
,"ecirc": 234
|
||||
,"euml": 235
|
||||
,"igrave": 236
|
||||
,"iacute": 237
|
||||
,"icirc": 238
|
||||
,"iuml": 239
|
||||
,"eth": 240
|
||||
,"ntilde": 241
|
||||
,"ograve": 242
|
||||
,"oacute": 243
|
||||
,"ocirc": 244
|
||||
,"otilde": 245
|
||||
,"ouml": 246
|
||||
,"divide": 247
|
||||
,"oslash": 248
|
||||
,"ugrave": 249
|
||||
,"uacute": 250
|
||||
,"ucirc": 251
|
||||
,"uuml": 252
|
||||
,"yacute": 253
|
||||
,"thorn": 254
|
||||
,"yuml": 255
|
||||
,"fnof": 402
|
||||
,"Alpha": 913
|
||||
,"Beta": 914
|
||||
,"Gamma": 915
|
||||
,"Delta": 916
|
||||
,"Epsilon": 917
|
||||
,"Zeta": 918
|
||||
,"Eta": 919
|
||||
,"Theta": 920
|
||||
,"Iota": 921
|
||||
,"Kappa": 922
|
||||
,"Lambda": 923
|
||||
,"Mu": 924
|
||||
,"Nu": 925
|
||||
,"Xi": 926
|
||||
,"Omicron": 927
|
||||
,"Pi": 928
|
||||
,"Rho": 929
|
||||
,"Sigma": 931
|
||||
,"Tau": 932
|
||||
,"Upsilon": 933
|
||||
,"Phi": 934
|
||||
,"Chi": 935
|
||||
,"Psi": 936
|
||||
,"Omega": 937
|
||||
,"alpha": 945
|
||||
,"beta": 946
|
||||
,"gamma": 947
|
||||
,"delta": 948
|
||||
,"epsilon": 949
|
||||
,"zeta": 950
|
||||
,"eta": 951
|
||||
,"theta": 952
|
||||
,"iota": 953
|
||||
,"kappa": 954
|
||||
,"lambda": 955
|
||||
,"mu": 956
|
||||
,"nu": 957
|
||||
,"xi": 958
|
||||
,"omicron": 959
|
||||
,"pi": 960
|
||||
,"rho": 961
|
||||
,"sigmaf": 962
|
||||
,"sigma": 963
|
||||
,"tau": 964
|
||||
,"upsilon": 965
|
||||
,"phi": 966
|
||||
,"chi": 967
|
||||
,"psi": 968
|
||||
,"omega": 969
|
||||
,"thetasym": 977
|
||||
,"upsih": 978
|
||||
,"piv": 982
|
||||
,"bull": 8226
|
||||
,"hellip": 8230
|
||||
,"prime": 8242
|
||||
,"Prime": 8243
|
||||
,"oline": 8254
|
||||
,"frasl": 8260
|
||||
,"weierp": 8472
|
||||
,"image": 8465
|
||||
,"real": 8476
|
||||
,"trade": 8482
|
||||
,"alefsym": 8501
|
||||
,"larr": 8592
|
||||
,"uarr": 8593
|
||||
,"rarr": 8594
|
||||
,"darr": 8595
|
||||
,"harr": 8596
|
||||
,"crarr": 8629
|
||||
,"lArr": 8656
|
||||
,"uArr": 8657
|
||||
,"rArr": 8658
|
||||
,"dArr": 8659
|
||||
,"hArr": 8660
|
||||
,"forall": 8704
|
||||
,"part": 8706
|
||||
,"exist": 8707
|
||||
,"empty": 8709
|
||||
,"nabla": 8711
|
||||
,"isin": 8712
|
||||
,"notin": 8713
|
||||
,"ni": 8715
|
||||
,"prod": 8719
|
||||
,"sum": 8721
|
||||
,"minus": 8722
|
||||
,"lowast": 8727
|
||||
,"radic": 8730
|
||||
,"prop": 8733
|
||||
,"infin": 8734
|
||||
,"ang": 8736
|
||||
,"and": 8743
|
||||
,"or": 8744
|
||||
,"cap": 8745
|
||||
,"cup": 8746
|
||||
,"int": 8747
|
||||
,"there4": 8756
|
||||
,"sim": 8764
|
||||
,"cong": 8773
|
||||
,"asymp": 8776
|
||||
,"ne": 8800
|
||||
,"equiv": 8801
|
||||
,"le": 8804
|
||||
,"ge": 8805
|
||||
,"sub": 8834
|
||||
,"sup": 8835
|
||||
,"nsub": 8836
|
||||
,"sube": 8838
|
||||
,"supe": 8839
|
||||
,"oplus": 8853
|
||||
,"otimes": 8855
|
||||
,"perp": 8869
|
||||
,"sdot": 8901
|
||||
,"lceil": 8968
|
||||
,"rceil": 8969
|
||||
,"lfloor": 8970
|
||||
,"rfloor": 8971
|
||||
,"lang": 9001
|
||||
,"rang": 9002
|
||||
,"loz": 9674
|
||||
,"spades": 9824
|
||||
,"clubs": 9827
|
||||
,"hearts": 9829
|
||||
,"diams": 9830
|
||||
,"quot": 34
|
||||
,"amp": 38
|
||||
,"lt": 60
|
||||
,"gt": 62
|
||||
,"OElig": 338
|
||||
,"oelig": 339
|
||||
,"Scaron": 352
|
||||
,"scaron": 353
|
||||
,"Yuml": 376
|
||||
,"circ": 710
|
||||
,"tilde": 732
|
||||
,"ensp": 8194
|
||||
,"emsp": 8195
|
||||
,"thinsp": 8201
|
||||
,"zwnj": 8204
|
||||
,"zwj": 8205
|
||||
,"lrm": 8206
|
||||
,"rlm": 8207
|
||||
,"ndash": 8211
|
||||
,"mdash": 8212
|
||||
,"lsquo": 8216
|
||||
,"rsquo": 8217
|
||||
,"sbquo": 8218
|
||||
,"ldquo": 8220
|
||||
,"rdquo": 8221
|
||||
,"bdquo": 8222
|
||||
,"dagger": 8224
|
||||
,"Dagger": 8225
|
||||
,"permil": 8240
|
||||
,"lsaquo": 8249
|
||||
,"rsaquo": 8250
|
||||
,"euro": 8364}
|
||||
@@ -0,0 +1,106 @@
|
||||
""" ElementTree integration for SimpleTAL
|
||||
|
||||
Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
If you make any bug fixes or feature enhancements please let me know!
|
||||
|
||||
The parseFile function in this module will return a Element object that
|
||||
implements simpleTALES.ContextVariable and makes XML documents available
|
||||
with the following path logic:
|
||||
- Accessing Element directly returns the Element.text value
|
||||
- Accessing Element/find and Element/findall passes the text
|
||||
(up to attribute accessor) to the corresponding Element function
|
||||
- Accessing the Element@name access the attribute "name"
|
||||
- Accessing Element/anotherElement is a short-cut for
|
||||
Element/find/anotherElement
|
||||
|
||||
Module Dependencies: simpleTALES, elementtree
|
||||
"""
|
||||
|
||||
from bongo.external.elementtree import ElementTree
|
||||
import bongo.external.simpleTALES
|
||||
|
||||
class SimpleElementTreeVar (ElementTree._ElementInterface, simpleTALES.ContextVariable):
|
||||
def __init__(self, tag, attrib):
|
||||
ElementTree._ElementInterface.__init__(self, tag, attrib)
|
||||
simpleTALES.ContextVariable.__init__(self)
|
||||
|
||||
def value (self, pathInfo = None):
|
||||
if (pathInfo is not None):
|
||||
pathIndex, paths = pathInfo
|
||||
ourParams = paths[pathIndex:]
|
||||
attributeName = None
|
||||
if (len (ourParams) > 0):
|
||||
# Look for attribute index
|
||||
if (ourParams[-1].startswith ('@')):
|
||||
# Attribute lookup
|
||||
attributeName = ourParams [-1][1:]
|
||||
ourParams = ourParams [:-1]
|
||||
# Do we do a find?
|
||||
activeElement = self
|
||||
if len (ourParams) > 0:
|
||||
# Look for a find or findall first
|
||||
if (ourParams [0] == 'find'):
|
||||
# Find the element if possible
|
||||
activeElement = self.find ("/".join (ourParams [1:]))
|
||||
elif (ourParams [0] == 'findall'):
|
||||
# Short cut this
|
||||
raise simpleTALES.ContextVariable (self.findall ("/".join (ourParams[1:])))
|
||||
else:
|
||||
# Assume that we wanted to use find
|
||||
activeElement = self.find ("/".join (ourParams))
|
||||
# Did we find an element and are we looking for an attribute?
|
||||
if (attributeName is not None and activeElement is not None):
|
||||
attrValue = activeElement.attrib.get (attributeName, None)
|
||||
raise simpleTALES.ContextVariable (attrValue)
|
||||
|
||||
# Just return the element
|
||||
if (activeElement is None):
|
||||
# Wrap it
|
||||
raise simpleTALES.ContextVariable (None)
|
||||
raise activeElement
|
||||
else:
|
||||
return self
|
||||
|
||||
def __unicode__ (self):
|
||||
return self.text
|
||||
|
||||
def __str__ (self):
|
||||
return str (self.text)
|
||||
|
||||
def parseFile (file):
|
||||
treeBuilder = ElementTree.TreeBuilder (element_factory = SimpleElementTreeVar)
|
||||
xmlTreeBuilder = ElementTree.XMLTreeBuilder (target=treeBuilder)
|
||||
|
||||
if (not hasattr (file, 'read')):
|
||||
ourFile = open (file)
|
||||
xmlTreeBuilder.feed (ourFile.read())
|
||||
ourFile.close()
|
||||
else:
|
||||
xmlTreeBuilder.feed (file.read())
|
||||
|
||||
return xmlTreeBuilder.close()
|
||||
|
||||
+1514
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,651 @@
|
||||
""" simpleTALES Implementation
|
||||
|
||||
Copyright (c) 2005 Colin Stewart (http://www.owlfish.com/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
If you make any bug fixes or feature enhancements please let me know!
|
||||
|
||||
The classes in this module implement the TALES specification, used
|
||||
by the simpleTAL module.
|
||||
|
||||
Module Dependencies: logging
|
||||
"""
|
||||
|
||||
import types, sys
|
||||
|
||||
try:
|
||||
import logging
|
||||
except:
|
||||
import DummyLogger as logging
|
||||
|
||||
import bongo.external.simpletal as simpletal
|
||||
|
||||
__version__ = simpletal.__version__
|
||||
|
||||
DEFAULTVALUE = "This represents a Default value."
|
||||
|
||||
class PathNotFoundException (Exception):
|
||||
pass
|
||||
|
||||
class ContextContentException (Exception):
|
||||
""" This is raised when invalid content has been placed into the Context object.
|
||||
For example using non-ascii characters instead of Unicode strings.
|
||||
"""
|
||||
pass
|
||||
|
||||
PATHNOTFOUNDEXCEPTION = PathNotFoundException()
|
||||
|
||||
class ContextVariable:
|
||||
def __init__ (self, value = None):
|
||||
self.ourValue = value
|
||||
|
||||
def value (self, currentPath=None):
|
||||
if (callable (self.ourValue)):
|
||||
return apply (self.ourValue, ())
|
||||
return self.ourValue
|
||||
|
||||
def rawValue (self):
|
||||
return self.ourValue
|
||||
|
||||
def __str__ (self):
|
||||
return repr (self.ourValue)
|
||||
|
||||
class RepeatVariable (ContextVariable):
|
||||
""" To be written"""
|
||||
def __init__ (self, sequence):
|
||||
ContextVariable.__init__ (self, 1)
|
||||
self.sequence = sequence
|
||||
self.position = 0
|
||||
self.map = None
|
||||
|
||||
def value (self, currentPath=None):
|
||||
if (self.map is None):
|
||||
self.createMap()
|
||||
return self.map
|
||||
|
||||
def rawValue (self):
|
||||
return self.value()
|
||||
|
||||
def getCurrentValue (self):
|
||||
return self.sequence [self.position]
|
||||
|
||||
def increment (self):
|
||||
self.position += 1
|
||||
if (self.position == len (self.sequence)):
|
||||
raise IndexError ("Repeat Finished")
|
||||
|
||||
def createMap (self):
|
||||
self.map = {}
|
||||
self.map ['index'] = self.getIndex
|
||||
self.map ['number'] = self.getNumber
|
||||
self.map ['even'] = self.getEven
|
||||
self.map ['odd'] = self.getOdd
|
||||
self.map ['start'] = self.getStart
|
||||
self.map ['end'] = self.getEnd
|
||||
# TODO: first and last need to be implemented.
|
||||
self.map ['length'] = len (self.sequence)
|
||||
self.map ['letter'] = self.getLowerLetter
|
||||
self.map ['Letter'] = self.getUpperLetter
|
||||
self.map ['roman'] = self.getLowerRoman
|
||||
self.map ['Roman'] = self.getUpperRoman
|
||||
|
||||
# Repeat implementation goes here
|
||||
def getIndex (self):
|
||||
return self.position
|
||||
|
||||
def getNumber (self):
|
||||
return self.position + 1
|
||||
|
||||
def getEven (self):
|
||||
if ((self.position % 2) != 0):
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def getOdd (self):
|
||||
if ((self.position % 2) == 0):
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def getStart (self):
|
||||
if (self.position == 0):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def getEnd (self):
|
||||
if (self.position == len (self.sequence) - 1):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def getLowerLetter (self):
|
||||
result = ""
|
||||
nextCol = self.position
|
||||
if (nextCol == 0):
|
||||
return 'a'
|
||||
while (nextCol > 0):
|
||||
nextCol, thisCol = divmod (nextCol, 26)
|
||||
result = chr (ord ('a') + thisCol) + result
|
||||
return result
|
||||
|
||||
def getUpperLetter (self):
|
||||
return self.getLowerLetter().upper()
|
||||
|
||||
def getLowerRoman (self):
|
||||
romanNumeralList = (('m', 1000)
|
||||
,('cm', 900)
|
||||
,('d', 500)
|
||||
,('cd', 400)
|
||||
,('c', 100)
|
||||
,('xc', 90)
|
||||
,('l', 50)
|
||||
,('xl', 40)
|
||||
,('x', 10)
|
||||
,('ix', 9)
|
||||
,('v', 5)
|
||||
,('iv', 4)
|
||||
,('i', 1)
|
||||
)
|
||||
if (self.position > 3999):
|
||||
# Roman numbers only supported up to 4000
|
||||
return ' '
|
||||
num = self.position + 1
|
||||
result = ""
|
||||
for roman, integer in romanNumeralList:
|
||||
while (num >= integer):
|
||||
result += roman
|
||||
num -= integer
|
||||
return result
|
||||
|
||||
def getUpperRoman (self):
|
||||
return self.getLowerRoman().upper()
|
||||
|
||||
class IteratorRepeatVariable (RepeatVariable):
|
||||
def __init__ (self, sequence):
|
||||
RepeatVariable.__init__ (self, sequence)
|
||||
self.curValue = None
|
||||
self.iterStatus = 0
|
||||
|
||||
def getCurrentValue (self):
|
||||
if (self.iterStatus == 0):
|
||||
self.iterStatus = 1
|
||||
try:
|
||||
self.curValue = self.sequence.next()
|
||||
except StopIteration, e:
|
||||
self.iterStatus = 2
|
||||
raise IndexError ("Repeat Finished")
|
||||
return self.curValue
|
||||
|
||||
def increment (self):
|
||||
# Need this for the repeat variable functions.
|
||||
self.position += 1
|
||||
try:
|
||||
self.curValue = self.sequence.next()
|
||||
except StopIteration, e:
|
||||
self.iterStatus = 2
|
||||
raise IndexError ("Repeat Finished")
|
||||
|
||||
def createMap (self):
|
||||
self.map = {}
|
||||
self.map ['index'] = self.getIndex
|
||||
self.map ['number'] = self.getNumber
|
||||
self.map ['even'] = self.getEven
|
||||
self.map ['odd'] = self.getOdd
|
||||
self.map ['start'] = self.getStart
|
||||
self.map ['end'] = self.getEnd
|
||||
# TODO: first and last need to be implemented.
|
||||
self.map ['length'] = sys.maxint
|
||||
self.map ['letter'] = self.getLowerLetter
|
||||
self.map ['Letter'] = self.getUpperLetter
|
||||
self.map ['roman'] = self.getLowerRoman
|
||||
self.map ['Roman'] = self.getUpperRoman
|
||||
|
||||
def getEnd (self):
|
||||
if (self.iterStatus == 2):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
class PathFunctionVariable (ContextVariable):
|
||||
def __init__ (self, func):
|
||||
ContextVariable.__init__ (self, value = func)
|
||||
self.func = func
|
||||
|
||||
def value (self, currentPath=None):
|
||||
if (currentPath is not None):
|
||||
index, paths = currentPath
|
||||
result = ContextVariable (apply (self.func, ('/'.join (paths[index:]),)))
|
||||
# Fast track the result
|
||||
raise result
|
||||
|
||||
class CachedFuncResult (ContextVariable):
|
||||
def value (self, currentPath=None):
|
||||
try:
|
||||
return self.cachedValue
|
||||
except:
|
||||
self.cachedValue = ContextVariable.value (self)
|
||||
return self.cachedValue
|
||||
|
||||
def clearCache (self):
|
||||
try:
|
||||
del self.cachedValue
|
||||
except:
|
||||
pass
|
||||
|
||||
class PythonPathFunctions:
|
||||
def __init__ (self, context):
|
||||
self.context = context
|
||||
|
||||
def path (self, expr):
|
||||
return self.context.evaluatePath (expr)
|
||||
|
||||
def string (self, expr):
|
||||
return self.context.evaluateString (expr)
|
||||
|
||||
def exists (self, expr):
|
||||
return self.context.evaluateExists (expr)
|
||||
|
||||
def nocall (self, expr):
|
||||
return self.context.evaluateNoCall (expr)
|
||||
|
||||
def test (self, *arguments):
|
||||
if (len (arguments) % 2):
|
||||
# We have an odd number of arguments - which means the last one is a default
|
||||
pairs = arguments[:-1]
|
||||
defaultValue = arguments[-1]
|
||||
else:
|
||||
# No default - so use None
|
||||
pairs = arguments
|
||||
defaultValue = None
|
||||
|
||||
index = 0
|
||||
while (index < len (pairs)):
|
||||
test = pairs[index]
|
||||
index += 1
|
||||
value = pairs[index]
|
||||
index += 1
|
||||
if (test):
|
||||
return value
|
||||
|
||||
return defaultValue
|
||||
|
||||
class Context:
|
||||
def __init__ (self, options=None, allowPythonPath=0):
|
||||
self.allowPythonPath = allowPythonPath
|
||||
self.globals = {}
|
||||
self.locals = {}
|
||||
self.localStack = []
|
||||
self.repeatStack = []
|
||||
self.populateDefaultVariables (options)
|
||||
self.log = logging.getLogger ("simpleTALES.Context")
|
||||
self.true = 1
|
||||
self.false = 0
|
||||
self.pythonPathFuncs = PythonPathFunctions (self)
|
||||
|
||||
def addRepeat (self, name, var, initialValue):
|
||||
# Pop the current repeat map onto the stack
|
||||
self.repeatStack.append (self.repeatMap)
|
||||
self.repeatMap = self.repeatMap.copy()
|
||||
self.repeatMap [name] = var
|
||||
# Map this repeatMap into the global space
|
||||
self.addGlobal ('repeat', self.repeatMap)
|
||||
|
||||
# Add in the locals
|
||||
self.pushLocals()
|
||||
self.setLocal (name, initialValue)
|
||||
|
||||
def removeRepeat (self, name):
|
||||
# Bring the old repeat map back
|
||||
self.repeatMap = self.repeatStack.pop()
|
||||
# Map this repeatMap into the global space
|
||||
self.addGlobal ('repeat', self.repeatMap)
|
||||
|
||||
def addGlobal (self, name, value):
|
||||
self.globals[name] = value
|
||||
|
||||
def pushLocals (self):
|
||||
# Push the current locals onto a stack so that we can safely over-ride them.
|
||||
self.localStack.append (self.locals)
|
||||
self.locals = self.locals.copy()
|
||||
|
||||
def setLocal (self, name, value):
|
||||
# Override the current local if present with the new one
|
||||
self.locals [name] = value
|
||||
|
||||
def popLocals (self):
|
||||
self.locals = self.localStack.pop()
|
||||
|
||||
def evaluate (self, expr, originalAtts = None):
|
||||
# Returns a ContextVariable
|
||||
#self.log.debug ("Evaluating %s" % expr)
|
||||
if (originalAtts is not None):
|
||||
# Call from outside
|
||||
self.globals['attrs'] = originalAtts
|
||||
suppressException = 1
|
||||
else:
|
||||
suppressException = 0
|
||||
|
||||
# Supports path, exists, nocall, not, and string
|
||||
expr = expr.strip ()
|
||||
try:
|
||||
if expr.startswith ('path:'):
|
||||
return self.evaluatePath (expr[5:].lstrip ())
|
||||
elif expr.startswith ('exists:'):
|
||||
return self.evaluateExists (expr[7:].lstrip())
|
||||
elif expr.startswith ('nocall:'):
|
||||
return self.evaluateNoCall (expr[7:].lstrip())
|
||||
elif expr.startswith ('not:'):
|
||||
return self.evaluateNot (expr[4:].lstrip())
|
||||
elif expr.startswith ('string:'):
|
||||
return self.evaluateString (expr[7:].lstrip())
|
||||
elif expr.startswith ('python:'):
|
||||
return self.evaluatePython (expr[7:].lstrip())
|
||||
else:
|
||||
# Not specified - so it's a path
|
||||
return self.evaluatePath (expr)
|
||||
except PathNotFoundException, e:
|
||||
if (suppressException):
|
||||
return None
|
||||
raise e
|
||||
|
||||
def evaluatePython (self, expr):
|
||||
if (not self.allowPythonPath):
|
||||
self.log.warn ("Parameter allowPythonPath is false. NOT Evaluating python expression %s" % expr)
|
||||
return self.false
|
||||
#self.log.debug ("Evaluating python expression %s" % expr)
|
||||
|
||||
globals={}
|
||||
for name, value in self.globals.items():
|
||||
if (isinstance (value, ContextVariable)): value = value.rawValue()
|
||||
globals [name] = value
|
||||
globals ['path'] = self.pythonPathFuncs.path
|
||||
globals ['string'] = self.pythonPathFuncs.string
|
||||
globals ['exists'] = self.pythonPathFuncs.exists
|
||||
globals ['nocall'] = self.pythonPathFuncs.nocall
|
||||
globals ['test'] = self.pythonPathFuncs.test
|
||||
|
||||
locals={}
|
||||
for name, value in self.locals.items():
|
||||
if (isinstance (value, ContextVariable)): value = value.rawValue()
|
||||
locals [name] = value
|
||||
|
||||
try:
|
||||
result = eval(expr, globals, locals)
|
||||
if (isinstance (result, ContextVariable)):
|
||||
return result.value()
|
||||
return result
|
||||
except Exception, e:
|
||||
# An exception occured evaluating the template, return the exception as text
|
||||
self.log.warn ("Exception occurred evaluating python path, exception: " + str (e))
|
||||
return "Exception: %s" % str (e)
|
||||
|
||||
def evaluatePath (self, expr):
|
||||
#self.log.debug ("Evaluating path expression %s" % expr)
|
||||
allPaths = expr.split ('|')
|
||||
if (len (allPaths) > 1):
|
||||
for path in allPaths:
|
||||
# Evaluate this path
|
||||
try:
|
||||
return self.evaluate (path.strip ())
|
||||
except PathNotFoundException, e:
|
||||
# Path didn't exist, try the next one
|
||||
pass
|
||||
# No paths evaluated - raise exception.
|
||||
raise PATHNOTFOUNDEXCEPTION
|
||||
else:
|
||||
# A single path - so let's evaluate it.
|
||||
# This *can* raise PathNotFoundException
|
||||
return self.traversePath (allPaths[0])
|
||||
|
||||
def evaluateExists (self, expr):
|
||||
#self.log.debug ("Evaluating %s to see if it exists" % expr)
|
||||
allPaths = expr.split ('|')
|
||||
# The first path is for us
|
||||
# Return true if this first bit evaluates, otherwise test the rest
|
||||
try:
|
||||
result = self.traversePath (allPaths[0], canCall = 0)
|
||||
return self.true
|
||||
except PathNotFoundException, e:
|
||||
# Look at the rest of the paths.
|
||||
pass
|
||||
|
||||
for path in allPaths[1:]:
|
||||
# Evaluate this path
|
||||
try:
|
||||
pathResult = self.evaluate (path.strip ())
|
||||
# If this is part of a "exists: path1 | exists: path2" path then we need to look at the actual result.
|
||||
if (pathResult):
|
||||
return self.true
|
||||
except PathNotFoundException, e:
|
||||
pass
|
||||
# If we get this far then there are *no* paths that exist.
|
||||
return self.false
|
||||
|
||||
def evaluateNoCall (self, expr):
|
||||
#self.log.debug ("Evaluating %s using nocall" % expr)
|
||||
allPaths = expr.split ('|')
|
||||
# The first path is for us
|
||||
try:
|
||||
return self.traversePath (allPaths[0], canCall = 0)
|
||||
except PathNotFoundException, e:
|
||||
# Try the rest of the paths.
|
||||
pass
|
||||
|
||||
for path in allPaths[1:]:
|
||||
# Evaluate this path
|
||||
try:
|
||||
return self.evaluate (path.strip ())
|
||||
except PathNotFoundException, e:
|
||||
pass
|
||||
# No path evaluated - raise error
|
||||
raise PATHNOTFOUNDEXCEPTION
|
||||
|
||||
def evaluateNot (self, expr):
|
||||
#self.log.debug ("Evaluating NOT value of %s" % expr)
|
||||
|
||||
# Evaluate what I was passed
|
||||
try:
|
||||
pathResult = self.evaluate (expr)
|
||||
except PathNotFoundException, e:
|
||||
# In SimpleTAL the result of "not: no/such/path" should be TRUE not FALSE.
|
||||
return self.true
|
||||
|
||||
if (pathResult is None):
|
||||
# Value was Nothing
|
||||
return self.true
|
||||
if (pathResult == DEFAULTVALUE):
|
||||
return self.false
|
||||
try:
|
||||
resultLen = len (pathResult)
|
||||
if (resultLen > 0):
|
||||
return self.false
|
||||
else:
|
||||
return self.true
|
||||
except:
|
||||
# Not a sequence object.
|
||||
pass
|
||||
if (not pathResult):
|
||||
return self.true
|
||||
# Everything else is true, so we return false!
|
||||
return self.false
|
||||
|
||||
def evaluateString (self, expr):
|
||||
#self.log.debug ("Evaluating String %s" % expr)
|
||||
result = ""
|
||||
skipCount = 0
|
||||
for position in xrange (0,len (expr)):
|
||||
if (skipCount > 0):
|
||||
skipCount -= 1
|
||||
else:
|
||||
if (expr[position] == '$'):
|
||||
try:
|
||||
if (expr[position + 1] == '$'):
|
||||
# Escaped $ sign
|
||||
result += '$'
|
||||
skipCount = 1
|
||||
elif (expr[position + 1] == '{'):
|
||||
# Looking for a path!
|
||||
endPos = expr.find ('}', position + 1)
|
||||
if (endPos > 0):
|
||||
path = expr[position + 2:endPos]
|
||||
# Evaluate the path - missing paths raise exceptions as normal.
|
||||
try:
|
||||
pathResult = self.evaluate (path)
|
||||
except PathNotFoundException, e:
|
||||
# This part of the path didn't evaluate to anything - leave blank
|
||||
pathResult = u''
|
||||
if (pathResult is not None):
|
||||
if (isinstance (pathResult, types.UnicodeType)):
|
||||
result += pathResult
|
||||
else:
|
||||
# THIS IS NOT A BUG!
|
||||
# Use Unicode in Context if you aren't using Ascii!
|
||||
result += unicode (pathResult)
|
||||
skipCount = endPos - position
|
||||
else:
|
||||
# It's a variable
|
||||
endPos = expr.find (' ', position + 1)
|
||||
if (endPos == -1):
|
||||
endPos = len (expr)
|
||||
path = expr [position + 1:endPos]
|
||||
# Evaluate the variable - missing paths raise exceptions as normal.
|
||||
try:
|
||||
pathResult = self.traversePath (path)
|
||||
except PathNotFoundException, e:
|
||||
# This part of the path didn't evaluate to anything - leave blank
|
||||
pathResult = u''
|
||||
if (pathResult is not None):
|
||||
if (isinstance (pathResult, types.UnicodeType)):
|
||||
result += pathResult
|
||||
else:
|
||||
# THIS IS NOT A BUG!
|
||||
# Use Unicode in Context if you aren't using Ascii!
|
||||
result += unicode (pathResult)
|
||||
skipCount = endPos - position - 1
|
||||
except IndexError, e:
|
||||
# Trailing $ sign - just suppress it
|
||||
self.log.warn ("Trailing $ detected")
|
||||
pass
|
||||
else:
|
||||
result += expr[position]
|
||||
return result
|
||||
|
||||
def traversePath (self, expr, canCall=1):
|
||||
# canCall only applies to the *final* path destination, not points down the path.
|
||||
# Check for and correct for trailing/leading quotes
|
||||
if (expr.startswith ('"') or expr.startswith ("'")):
|
||||
if (expr.endswith ('"') or expr.endswith ("'")):
|
||||
expr = expr [1:-1]
|
||||
else:
|
||||
expr = expr [1:]
|
||||
elif (expr.endswith ('"') or expr.endswith ("'")):
|
||||
expr = expr [0:-1]
|
||||
pathList = expr.split ('/')
|
||||
|
||||
path = pathList[0]
|
||||
if path.startswith ('?'):
|
||||
path = path[1:]
|
||||
if self.locals.has_key(path):
|
||||
path = self.locals[path]
|
||||
if (isinstance (path, ContextVariable)): path = path.value()
|
||||
elif (callable (path)):path = apply (path, ())
|
||||
|
||||
elif self.globals.has_key(path):
|
||||
path = self.globals[path]
|
||||
if (isinstance (path, ContextVariable)): path = path.value()
|
||||
elif (callable (path)):path = apply (path, ())
|
||||
#self.log.debug ("Dereferenced to %s" % path)
|
||||
if self.locals.has_key(path):
|
||||
val = self.locals[path]
|
||||
elif self.globals.has_key(path):
|
||||
val = self.globals[path]
|
||||
else:
|
||||
# If we can't find it then raise an exception
|
||||
raise PATHNOTFOUNDEXCEPTION
|
||||
index = 1
|
||||
for path in pathList[1:]:
|
||||
#self.log.debug ("Looking for path element %s" % path)
|
||||
if path.startswith ('?'):
|
||||
path = path[1:]
|
||||
if self.locals.has_key(path):
|
||||
path = self.locals[path]
|
||||
if (isinstance (path, ContextVariable)): path = path.value()
|
||||
elif (callable (path)):path = apply (path, ())
|
||||
elif self.globals.has_key(path):
|
||||
path = self.globals[path]
|
||||
if (isinstance (path, ContextVariable)): path = path.value()
|
||||
elif (callable (path)):path = apply (path, ())
|
||||
#self.log.debug ("Dereferenced to %s" % path)
|
||||
try:
|
||||
if (isinstance (val, ContextVariable)): temp = val.value((index,pathList))
|
||||
elif (callable (val)):temp = apply (val, ())
|
||||
else: temp = val
|
||||
except ContextVariable, e:
|
||||
# Fast path for those functions that return values
|
||||
return e.value()
|
||||
|
||||
if (hasattr (temp, path)):
|
||||
val = getattr (temp, path)
|
||||
else:
|
||||
try:
|
||||
try:
|
||||
val = temp[path]
|
||||
except TypeError:
|
||||
val = temp[int(path)]
|
||||
except:
|
||||
#self.log.debug ("Not found.")
|
||||
raise PATHNOTFOUNDEXCEPTION
|
||||
index = index + 1
|
||||
#self.log.debug ("Found value %s" % str (val))
|
||||
if (canCall):
|
||||
try:
|
||||
if (isinstance (val, ContextVariable)): result = val.value((index,pathList))
|
||||
elif (callable (val)):result = apply (val, ())
|
||||
else: result = val
|
||||
except ContextVariable, e:
|
||||
# Fast path for those functions that return values
|
||||
return e.value()
|
||||
else:
|
||||
if (isinstance (val, ContextVariable)): result = val.realValue
|
||||
else: result = val
|
||||
return result
|
||||
|
||||
def __str__ (self):
|
||||
return "Globals: " + str (self.globals) + "Locals: " + str (self.locals)
|
||||
|
||||
def populateDefaultVariables (self, options):
|
||||
vars = {}
|
||||
self.repeatMap = {}
|
||||
vars['nothing'] = None
|
||||
vars['default'] = DEFAULTVALUE
|
||||
vars['options'] = options
|
||||
# To start with there are no repeats
|
||||
vars['repeat'] = self.repeatMap
|
||||
vars['attrs'] = None
|
||||
|
||||
# Add all of these to the global context
|
||||
for name in vars.keys():
|
||||
self.addGlobal (name,vars[name])
|
||||
|
||||
# Add also under CONTEXTS
|
||||
self.addGlobal ('CONTEXTS', vars)
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
""" simpleTALUtils
|
||||
|
||||
Copyright (c) 2005 Colin Stewart (http://www.owlfish.com/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
If you make any bug fixes or feature enhancements please let me know!
|
||||
|
||||
This module is holds utilities that make using SimpleTAL easier.
|
||||
Initially this is just the HTMLStructureCleaner class, used to clean
|
||||
up HTML that can then be used as 'structure' content.
|
||||
|
||||
Module Dependencies: None
|
||||
"""
|
||||
|
||||
import StringIO, os, stat, threading, sys, codecs, sgmllib, cgi, re, types
|
||||
import bongo.external.simpletal, bongo.external.simpleTAL
|
||||
|
||||
__version__ = simpletal.__version__
|
||||
|
||||
# This is used to check for already escaped attributes.
|
||||
ESCAPED_TEXT_REGEX=re.compile (r"\&\S+?;")
|
||||
|
||||
class HTMLStructureCleaner (sgmllib.SGMLParser):
|
||||
""" A helper class that takes HTML content and parses it, so converting
|
||||
any stray '&', '<', or '>' symbols into their respective entity references.
|
||||
"""
|
||||
def clean (self, content, encoding=None):
|
||||
""" Takes the HTML content given, parses it, and converts stray markup.
|
||||
The content can be either:
|
||||
- A unicode string, in which case the encoding parameter is not required
|
||||
- An ordinary string, in which case the encoding will be used
|
||||
- A file-like object, in which case the encoding will be used if present
|
||||
|
||||
The method returns a unicode string which is suitable for addition to a
|
||||
simpleTALES.Context object.
|
||||
"""
|
||||
if (isinstance (content, types.StringType)):
|
||||
# Not unicode, convert
|
||||
converter = codecs.lookup (encoding)[1]
|
||||
file = StringIO.StringIO (converter (content)[0])
|
||||
elif (isinstance (content, types.UnicodeType)):
|
||||
file = StringIO.StringIO (content)
|
||||
else:
|
||||
# Treat it as a file type object - and convert it if we have an encoding
|
||||
if (encoding is not None):
|
||||
converterStream = codecs.lookup (encoding)[2]
|
||||
file = converterStream (content)
|
||||
else:
|
||||
file = content
|
||||
|
||||
self.outputFile = StringIO.StringIO (u"")
|
||||
self.feed (file.read())
|
||||
self.close()
|
||||
return self.outputFile.getvalue()
|
||||
|
||||
def unknown_starttag (self, tag, attributes):
|
||||
self.outputFile.write (tagAsText (tag, attributes))
|
||||
|
||||
def unknown_endtag (self, tag):
|
||||
self.outputFile.write ('</' + tag + '>')
|
||||
|
||||
def handle_data (self, data):
|
||||
self.outputFile.write (cgi.escape (data))
|
||||
|
||||
def handle_charref (self, ref):
|
||||
self.outputFile.write (u'&#%s;' % ref)
|
||||
|
||||
def handle_entityref (self, ref):
|
||||
self.outputFile.write (u'&%s;' % ref)
|
||||
|
||||
|
||||
class FastStringOutput:
|
||||
""" A very simple StringIO replacement that only provides write() and getvalue()
|
||||
and is around 6% faster than StringIO.
|
||||
"""
|
||||
def __init__ (self):
|
||||
self.data = []
|
||||
|
||||
def write (self, data):
|
||||
self.data.append (data)
|
||||
|
||||
def getvalue (self):
|
||||
return "".join (self.data)
|
||||
|
||||
class TemplateCache:
|
||||
""" A TemplateCache is a multi-thread safe object that caches compiled templates.
|
||||
This cache only works with file based templates, the ctime of the file is
|
||||
checked on each hit, if the file has changed the template is re-compiled.
|
||||
"""
|
||||
def __init__ (self):
|
||||
self.templateCache = {}
|
||||
self.cacheLock = threading.Lock()
|
||||
self.hits = 0
|
||||
self.misses = 0
|
||||
|
||||
def getTemplate (self, name, inputEncoding='ISO-8859-1'):
|
||||
""" Name should be the path of a template file. If the path ends in 'xml' it is treated
|
||||
as an XML Template, otherwise it's treated as an HTML Template. If the template file
|
||||
has changed since the last cache it will be re-compiled.
|
||||
|
||||
inputEncoding is only used for HTML templates, and should be the encoding that the template
|
||||
is stored in.
|
||||
"""
|
||||
if (self.templateCache.has_key (name)):
|
||||
template, oldctime = self.templateCache [name]
|
||||
ctime = os.stat (name)[stat.ST_MTIME]
|
||||
if (oldctime == ctime):
|
||||
# Cache hit!
|
||||
self.hits += 1
|
||||
return template
|
||||
# Cache miss, let's cache this template
|
||||
return self._cacheTemplate_ (name, inputEncoding)
|
||||
|
||||
def getXMLTemplate (self, name):
|
||||
""" Name should be the path of an XML template file.
|
||||
"""
|
||||
if (self.templateCache.has_key (name)):
|
||||
template, oldctime = self.templateCache [name]
|
||||
ctime = os.stat (name)[stat.ST_MTIME]
|
||||
if (oldctime == ctime):
|
||||
# Cache hit!
|
||||
self.hits += 1
|
||||
return template
|
||||
# Cache miss, let's cache this template
|
||||
return self._cacheTemplate_ (name, None, xmlTemplate=1)
|
||||
|
||||
def _cacheTemplate_ (self, name, inputEncoding, xmlTemplate=0):
|
||||
self.cacheLock.acquire ()
|
||||
try:
|
||||
tempFile = open (name, 'r')
|
||||
if (xmlTemplate):
|
||||
# We know it is XML
|
||||
template = simpleTAL.compileXMLTemplate (tempFile)
|
||||
else:
|
||||
# We have to guess...
|
||||
firstline = tempFile.readline()
|
||||
tempFile.seek(0)
|
||||
if (name [-3:] == "xml") or (firstline.strip ()[:5] == '<?xml') or (firstline [:9] == '<!DOCTYPE' and firstline.find('XHTML') != -1):
|
||||
template = simpleTAL.compileXMLTemplate (tempFile)
|
||||
else:
|
||||
template = simpleTAL.compileHTMLTemplate (tempFile, inputEncoding)
|
||||
tempFile.close()
|
||||
self.templateCache [name] = (template, os.stat (name)[stat.ST_MTIME])
|
||||
self.misses += 1
|
||||
except Exception, e:
|
||||
self.cacheLock.release()
|
||||
raise e
|
||||
|
||||
self.cacheLock.release()
|
||||
return template
|
||||
|
||||
def tagAsText (tag,atts):
|
||||
result = "<" + tag
|
||||
for name,value in atts:
|
||||
if (ESCAPED_TEXT_REGEX.search (value) is not None):
|
||||
# We already have some escaped characters in here, so assume it's all valid
|
||||
result += ' %s="%s"' % (name, value)
|
||||
else:
|
||||
result += ' %s="%s"' % (name, cgi.escape (value))
|
||||
result += ">"
|
||||
return result
|
||||
|
||||
class MacroExpansionInterpreter (simpleTAL.TemplateInterpreter):
|
||||
def __init__ (self):
|
||||
simpleTAL.TemplateInterpreter.__init__ (self)
|
||||
# Override the standard interpreter way of doing things.
|
||||
self.macroStateStack = []
|
||||
self.commandHandler [simpleTAL.TAL_DEFINE] = self.cmdNoOp
|
||||
self.commandHandler [simpleTAL.TAL_CONDITION] = self.cmdNoOp
|
||||
self.commandHandler [simpleTAL.TAL_REPEAT] = self.cmdNoOp
|
||||
self.commandHandler [simpleTAL.TAL_CONTENT] = self.cmdNoOp
|
||||
self.commandHandler [simpleTAL.TAL_ATTRIBUTES] = self.cmdNoOp
|
||||
self.commandHandler [simpleTAL.TAL_OMITTAG] = self.cmdNoOp
|
||||
self.commandHandler [simpleTAL.TAL_START_SCOPE] = self.cmdStartScope
|
||||
self.commandHandler [simpleTAL.TAL_OUTPUT] = self.cmdOutput
|
||||
self.commandHandler [simpleTAL.TAL_STARTTAG] = self.cmdOutputStartTag
|
||||
self.commandHandler [simpleTAL.TAL_ENDTAG_ENDSCOPE] = self.cmdEndTagEndScope
|
||||
self.commandHandler [simpleTAL.METAL_USE_MACRO] = self.cmdUseMacro
|
||||
self.commandHandler [simpleTAL.METAL_DEFINE_SLOT] = self.cmdDefineSlot
|
||||
self.commandHandler [simpleTAL.TAL_NOOP] = self.cmdNoOp
|
||||
|
||||
self.inMacro = None
|
||||
self.macroArg = None
|
||||
# Original cmdOutput
|
||||
# Original cmdEndTagEndScope
|
||||
|
||||
def popProgram (self):
|
||||
self.inMacro = self.macroStateStack.pop()
|
||||
simpleTAL.TemplateInterpreter.popProgram (self)
|
||||
|
||||
def pushProgram (self):
|
||||
self.macroStateStack.append (self.inMacro)
|
||||
simpleTAL.TemplateInterpreter.pushProgram (self)
|
||||
|
||||
def cmdOutputStartTag (self, command, args):
|
||||
newAtts = []
|
||||
for att, value in self.originalAttributes.items():
|
||||
if (self.macroArg is not None and att == "metal:define-macro"):
|
||||
newAtts.append (("metal:use-macro",self.macroArg))
|
||||
elif (self.inMacro and att=="metal:define-slot"):
|
||||
newAtts.append (("metal:fill-slot", value))
|
||||
else:
|
||||
newAtts.append ((att, value))
|
||||
self.macroArg = None
|
||||
self.currentAttributes = newAtts
|
||||
simpleTAL.TemplateInterpreter.cmdOutputStartTag (self, command, args)
|
||||
|
||||
def cmdUseMacro (self, command, args):
|
||||
simpleTAL.TemplateInterpreter.cmdUseMacro (self, command, args)
|
||||
if (self.tagContent is not None):
|
||||
# We have a macro, add the args to the in-macro list
|
||||
self.inMacro = 1
|
||||
self.macroArg = args[0]
|
||||
|
||||
def cmdEndTagEndScope (self, command, args):
|
||||
# Args: tagName, omitFlag
|
||||
if (self.tagContent is not None):
|
||||
contentType, resultVal = self.tagContent
|
||||
if (contentType):
|
||||
if (isinstance (resultVal, simpleTAL.Template)):
|
||||
# We have another template in the context, evaluate it!
|
||||
# Save our state!
|
||||
self.pushProgram()
|
||||
resultVal.expandInline (self.context, self.file, self)
|
||||
# Restore state
|
||||
self.popProgram()
|
||||
# End of the macro expansion (if any) so clear the parameters
|
||||
self.slotParameters = {}
|
||||
# End of the macro
|
||||
self.inMacro = 0
|
||||
else:
|
||||
if (isinstance (resultVal, types.UnicodeType)):
|
||||
self.file.write (resultVal)
|
||||
elif (isinstance (resultVal, types.StringType)):
|
||||
self.file.write (unicode (resultVal, 'ascii'))
|
||||
else:
|
||||
self.file.write (unicode (str (resultVal), 'ascii'))
|
||||
else:
|
||||
if (isinstance (resultVal, types.UnicodeType)):
|
||||
self.file.write (cgi.escape (resultVal))
|
||||
elif (isinstance (resultVal, types.StringType)):
|
||||
self.file.write (cgi.escape (unicode (resultVal, 'ascii')))
|
||||
else:
|
||||
self.file.write (cgi.escape (unicode (str (resultVal), 'ascii')))
|
||||
|
||||
if (self.outputTag and not args[1]):
|
||||
self.file.write ('</' + args[0] + '>')
|
||||
|
||||
if (self.movePCBack is not None):
|
||||
self.programCounter = self.movePCBack
|
||||
return
|
||||
|
||||
if (self.localVarsDefined):
|
||||
self.context.popLocals()
|
||||
|
||||
self.movePCForward,self.movePCBack,self.outputTag,self.originalAttributes,self.currentAttributes,self.repeatVariable,self.tagContent,self.localVarsDefined = self.scopeStack.pop()
|
||||
self.programCounter += 1
|
||||
|
||||
def ExpandMacros (context, template, outputEncoding="ISO-8859-1"):
|
||||
out = StringIO.StringIO()
|
||||
interp = MacroExpansionInterpreter()
|
||||
interp.initialise (context, out)
|
||||
template.expand (context, out, outputEncoding=outputEncoding, interpreter=interp)
|
||||
# StringIO returns unicode, so we need to turn it back into native string
|
||||
result = out.getvalue()
|
||||
reencoder = codecs.lookup (outputEncoding)[0]
|
||||
return reencoder (result)[0]
|
||||
|
||||
@@ -16,6 +16,7 @@ class DocTypes:
|
||||
Addressbook = 0x0004
|
||||
Conversation = 0x0005
|
||||
Calendar = 0x0006
|
||||
Config = 0x0007
|
||||
|
||||
Folder = 0x1000
|
||||
Shared = 0x2000
|
||||
|
||||
Reference in New Issue
Block a user