1
2
3
4
5
6
7
8
9
10
11
12
13 """DTML Utilities
14
15 $Id: DT_Util.py 1069 2008-11-13 21:55:43Z stefan $"""
16
17 import re
18 import VSEval
19
20 str=__builtins__['str']
21
22 ParseError='Document Template Parse Error'
23 ValidationError='Unauthorized'
24
25
26 -def html_quote(v, name='(Unknown name)', md={},
27 character_entities=(
28 (('&'), '&'),
29 (('<'), '<' ),
30 (('>'), '>' ),
31 (('"'), '"'))):
32 text=str(v)
33 for re,name in character_entities:
34 if text.find(re) >= 0: text=text.split(re).join(name)
35 return text
36
37 -def int_param(params,md,name,default=0, st=type('')):
38 v = params.get(name, default)
39 if v:
40 try:
41 v = int(v)
42 except:
43 v = md[v]
44 if isinstance(v, str):
45 v = int(v)
46 return v or 0
47
48 _marker=[]
49
72
88
97
99 v=len(indexes)
100 if v==2:
101 v=seq[indexes[0]:indexes[1]]
102 elif v==1:
103 v=seq[indexes[0]:]
104 else: v=seq[:]
105
106 if type(seq) is type(''): return v
107
108 validate=md.validate
109 if validate is not None:
110 for e in v:
111 if not validate(seq,seq,None,e,md):
112 raise ValidationError, 'unauthorized access to slice member'
113
114 return v
115
117
118 RANGELIMIT = 1000
119 if not len(args):
120 iStart, iEnd, iStep = 0, iFirst, 1
121 elif len(args) == 1:
122 iStart, iEnd, iStep = iFirst, args[0], 1
123 elif len(args) == 2:
124 iStart, iEnd, iStep = iFirst, args[0], args[1]
125 else:
126 raise AttributeError, 'range() requires 1-3 int arguments'
127 if iStep == 0: raise ValueError, 'zero step for range()'
128 iLen = int((iEnd - iStart) / iStep)
129 if iLen < 0: iLen = 0
130 if iLen >= RANGELIMIT: raise ValueError, 'range() too large'
131 return range(iStart, iEnd, iStep)
132
133 import string, math, random
134
135 try:
136 import ExtensionClass
137 from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks
138 except: from pDocumentTemplate import InstanceDict, TemplateDict, render_blocks
139
140
141 d=TemplateDict.__dict__
142 for name in ('None', 'abs', 'chr', 'divmod', 'float', 'hash', 'hex', 'int',
143 'len', 'max', 'min', 'oct', 'ord', 'round', 'str'):
144 d[name]=__builtins__[name]
145 d['string']=string
146 d['math']=math
147 d['random']=random
148
150 if not z: raise ValueError, 'pow(x, y, z) with z==0'
151 return pow(x,y,z)
152
153 d['pow']=careful_pow
154
155 try:
156 import random
157 d['random']=random
158 except: pass
159
160 try:
161 import DateTime
162 d['DateTime']=DateTime.DateTime
163 except: pass
164
165 -def test(self, *args):
166 l=len(args)
167 for i in range(1, l, 2):
168 if args[i-1]: return args[i]
169
170 if l%2: return args[-1]
171
172 d['test']=test
173
176
177 d['attr']=obsolete_attr
178 d['getattr']=careful_getattr
179 d['hasattr']=careful_hasattr
180 d['range']=careful_range
181
182
183
184
186 """Create a tuple consisting of a single instance whose attributes are
187 provided as keyword arguments."""
188 if getattr(self, '__class__', None) != TemplateDict:
189 raise TypeError,'''A call was made to DT_Util.namespace() with an
190 incorrect "self" argument. It could be caused by a product which
191 is not yet compatible with this version of Zope. The traceback
192 information may contain more details.)'''
193 return apply(self, (), kw)
194
195 d['namespace']=namespace
196
198 "Render an object in the way done by the 'name' attribute"
199 if hasattr(v, '__render_with_namespace__'):
200 v = v.__render_with_namespace__(self)
201 else:
202 vbase = getattr(v, 'aq_base', v)
203 if callable(vbase):
204 if getattr(vbase, 'isDocTemp', 0):
205 v = v(None, self)
206 else:
207 v = v()
208 return v
209
210 d['render']=render
211
212 expr_globals={
213 '__builtins__':{},
214 '__guarded_mul__': VSEval.careful_mul,
215 '__guarded_getattr__': careful_getattr,
216 '__guarded_getitem__': careful_getitem,
217 '__guarded_getslice__': careful_getslice,
218 }
219
220 -class Eval(VSEval.Eval):
221
222 - def eval(self, mapping):
235
236
237 -def name_param(params,tag='',expr=0, attr='name', default_unnamed=1):
238 used=params.has_key
239 __traceback_info__=params, tag, expr, attr
240
241
242
243
244
245
246 if used(''):
247 v=params['']
248
249 if v[:1]=='"' and v[-1:]=='"' and len(v) > 1:
250 if used(attr):
251 raise ParseError, ('%s and expr given' % attr, tag)
252 if expr:
253 if used('expr'):
254 raise ParseError, ('two exprs given', tag)
255 v=v[1:-1]
256 try: expr=Eval(v, expr_globals)
257 except SyntaxError, v:
258 raise ParseError, (
259 '<strong>Expression (Python) Syntax error</strong>:'
260 '\n<pre>\n%s\n</pre>\n' % v[0],
261 tag)
262 return v, expr
263 else: raise ParseError, (
264 'The "..." shorthand for expr was used in a tag '
265 'that doesn\'t support expr attributes.',
266 tag)
267
268 else:
269 if used(attr):
270 raise ParseError, ('Two %s values were given' % attr, tag)
271 if expr:
272 if used('expr'):
273
274 raise ParseError, ('%s and expr given' % attr, tag)
275 return params[''],None
276 return params['']
277
278 elif used(attr):
279 if expr:
280 if used('expr'):
281 raise ParseError, ('%s and expr given' % attr, tag)
282 return params[attr],None
283 return params[attr]
284 elif expr and used('expr'):
285 name=params['expr']
286 expr=Eval(name, expr_globals)
287 return name, expr
288
289 raise ParseError, ('No %s given' % attr, tag)
290
291 Expr_doc="""
292
293
294 Python expression support
295
296 Several document template tags, including 'var', 'in', 'if', 'else',
297 and 'elif' provide support for using Python expressions via an
298 'expr' tag attribute.
299
300 Expressions may be used where a simple variable value is
301 inadequate. For example, an expression might be used to test
302 whether a variable is greater than some amount::
303
304 <!--#if expr="age > 18"-->
305
306 or to transform some basic data::
307
308 <!--#var expr="phone[:3]"-->
309
310 Objects available in the document templates namespace may be used.
311 Subobjects of these objects may be used as well, although subobject
312 access is restricted by the optional validation method.
313
314 In addition, a special additional name, '_', is available. The '_'
315 variable provides access to the document template namespace as a
316 mapping object. This variable can be useful for accessing objects
317 in a document template namespace that have names that are not legal
318 Python variable names::
319
320 <!--#var expr="_['sequence-number']*5"-->
321
322 This variable also has attributes that provide access to standard
323 utility objects. These attributes include:
324
325 - The objects: 'None', 'abs', 'chr', 'divmod', 'float', 'hash',
326 'hex', 'int', 'len', 'max', 'min', 'oct', 'ord', 'pow',
327 'round', and 'str' from the standard Python builtin module.
328
329 - Special security-aware versions of 'getattr' and 'hasattr',
330
331 - The Python 'string', 'math', and 'random' modules, and
332
333 - A special function, 'test', that supports if-then expressions.
334 The 'test' function accepts any number of arguments. If the
335 first argument is true, then the second argument is returned,
336 otherwise if the third argument is true, then the fourth
337 argument is returned, and so on. If there is an odd number of
338 arguments, then the last argument is returned in the case that
339 none of the tested arguments is true, otherwise None is
340 returned.
341
342 For example, to convert a value to lower case::
343
344 <!--#var expr="_.string.lower(title)"-->
345
346 """
347
348 ListType=type([])
349 -def parse_params(text,
350 result=None,
351 tag='',
352 unparmre=re.compile('([\000- ]*([^\000- ="]+))'),
353 qunparmre=re.compile('([\000- ]*("[^"]*"))'),
354 parmre=re.compile('([\000- ]*([^\000- ="]+)=([^\000- ="]+))'),
355 qparmre=re.compile('([\000- ]*([^\000- ="]+)="([^"]*)")'),
356 **parms):
357
358 """Parse tag parameters
359
360 The format of tag parameters consists of 1 or more parameter
361 specifications separated by whitespace. Each specification
362 consists of an unnamed and unquoted value, a valueless name, or a
363 name-value pair. A name-value pair consists of a name and a
364 quoted or unquoted value separated by an '='.
365
366 The input parameter, text, gives the text to be parsed. The
367 keyword parameters give valid parameter names and default values.
368
369 If a specification is not a name-value pair and it is not the
370 first specification and it is a
371 valid parameter name, then it is treated as a name-value pair with
372 a value as given in the keyword argument. Otherwise, if it is not
373 a name-value pair, it is treated as an unnamed value.
374
375 The data are parsed into a dictionary mapping names to values.
376 Unnamed values are mapped from the name '""'. Only one value may
377 be given for a name and there may be only one unnamed value. """
378
379 result=result or {}
380
381
382
383
384 mo_p = parmre.match(text)
385 mo_q = qparmre.match(text)
386 mo_unp = unparmre.match(text)
387 mo_unq = qunparmre.match(text)
388
389 if mo_p:
390 name=mo_p.group(2).lower()
391 value=mo_p.group(3)
392 l=len(mo_p.group(1))
393 elif mo_q:
394 name=mo_q.group(2).lower()
395 value=mo_q.group(3)
396 l=len(mo_q.group(1))
397 elif mo_unp:
398 name=mo_unp.group(2)
399 l=len(mo_unp.group(1))
400 if result:
401 if parms.has_key(name):
402 if parms[name] is None: raise ParseError, (
403 'Attribute %s requires a value' % name, tag)
404
405 result[name]=parms[name]
406 else: raise ParseError, (
407 'Invalid attribute name, "%s"' % name, tag)
408 else:
409 result['']=name
410 return parse_params(text[l:],result,**parms)
411 elif mo_unq:
412 name=mo_unq.group(2)
413 l=len(mo_unq.group(1))
414 if result: raise ParseError, (
415 'Invalid attribute name, "%s"' % name, tag)
416 else: result['']=name
417 return parse_params(text[l:],result,**parms)
418 else:
419 if not text or not text.strip(): return result
420 raise ParseError, ('invalid parameter: "%s"' % text, tag)
421
422 if not parms.has_key(name):
423 raise ParseError, (
424 'Invalid attribute name, "%s"' % name, tag)
425
426 if result.has_key(name):
427 p=parms[name]
428 if type(p) is not ListType or p:
429 raise ParseError, (
430 'Duplicate values for attribute "%s"' % name, tag)
431
432 result[name]=value
433
434 text=text[l:].strip()
435 if text: return parse_params(text,result,**parms)
436 else: return result
437