Commit ca3753b2 authored by Hanno Schlichting's avatar Hanno Schlichting

Moved DocumentTemplate and TreeDisplay to their own distro

parent 13bc9b23
......@@ -39,6 +39,7 @@ eggs =
AccessControl
Acquisition
DateTime
DocumentTemplate
ExtensionClass
Missing
MultiMapping
......
......@@ -31,13 +31,6 @@ setup(name='Zope2',
package_dir={'': 'src'},
ext_modules=[
# DocumentTemplate
Extension(
name='DocumentTemplate.cDocumentTemplate',
include_dirs=['include', 'src'],
sources=['src/DocumentTemplate/cDocumentTemplate.c'],
depends=['include/ExtensionClass/ExtensionClass.h']),
# indexes
Extension(
name='Products.ZCTextIndex.stopper',
......@@ -52,6 +45,7 @@ setup(name='Zope2',
'AccessControl',
'Acquisition',
'DateTime',
'DocumentTemplate',
'ExtensionClass',
'Missing',
'MultiMapping',
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""HTML formated DocumentTemplates
$Id$"""
import re
from DocumentTemplate.DT_String import String, FileMixin
from DocumentTemplate.DT_Util import ParseError, str
class dtml_re_class:
""" This needs to be replaced before 2.4. It's a hackaround. """
def search(self, text, start=0,
name_match=re.compile('[\000- ]*[a-zA-Z]+[\000- ]*').match,
end_match=re.compile('[\000- ]*(/|end)', re.I).match,
start_search=re.compile('[<&]').search,
ent_name=re.compile('[-a-zA-Z0-9_.]+').match,
):
while 1:
mo = start_search(text,start)
if mo is None: return None
s = mo.start(0)
if text[s:s+5] == '<!--#':
n=s+5
e=text.find('-->',n)
if e < 0: return None
en=3
mo =end_match(text,n)
if mo is not None:
l = mo.end(0) - mo.start(0)
end=text[n:n+l].strip()
n=n+l
else: end=''
elif text[s:s+6] == '<dtml-':
e=n=s+6
while 1:
e=text.find('>',e+1)
if e < 0: return None
if len(text[n:e].split('"'))%2:
# check for even number of "s inside
break
en=1
end=''
elif text[s:s+7] == '</dtml-':
e=n=s+7
while 1:
e=text.find('>',e+1)
if e < 0: return None
if len(text[n:e].split('"'))%2:
# check for even number of "s inside
break
en=1
end='/'
else:
if text[s:s+5] == '&dtml' and text[s+5] in '.-':
n=s+6
e=text.find(';',n)
if e >= 0:
args=text[n:e]
l=len(args)
mo = ent_name(args)
if mo is not None:
if mo.end(0)-mo.start(0) == l:
d=self.__dict__
if text[s+5]=='-':
d[1]=d['end']=''
d[2]=d['name']='var'
d[0]=text[s:e+1]
d[3]=d['args']=args+' html_quote'
self._start = s
return self
else:
nn=args.find('-')
if nn >= 0 and nn < l-1:
d[1]=d['end']=''
d[2]=d['name']='var'
d[0]=text[s:e+1]
args=args[nn+1:]+' '+ \
args[:nn].replace('.',' ')
d[3]=d['args']=args
self._start = s
return self
start=s+1
continue
break
mo = name_match(text,n)
if mo is None: return None
l = mo.end(0) - mo.start(0)
a=n+l
name=text[n:a].strip()
args=text[a:e].strip()
d=self.__dict__
d[0]=text[s:e+en]
d[1]=d['end']=end
d[2]=d['name']=name
d[3]=d['args']=args
self._start = s
return self
def group(self, *args):
get=self.__dict__.get
if len(args)==1:
return get(args[0])
return tuple(map(get, args))
def start(self, *args):
return self._start
class HTML(String):
"""HTML Document Templates
HTML Document templates use HTML server-side-include syntax,
rather than Python format-string syntax. Here's a simple example:
<!--#in results-->
<!--#var name-->
<!--#/in-->
HTML document templates quote HTML tags in source when the
template is converted to a string. This is handy when templates
are inserted into HTML editing forms.
"""
tagre__roles__=()
def tagre(self):
return dtml_re_class()
parseTag__roles__=()
def parseTag(self, match_ob, command=None, sargs=''):
"""Parse a tag using an already matched re
Return: tag, args, command, coname
where: tag is the tag,
args is the tag\'s argument string,
command is a corresponding command info structure if the
tag is a start tag, or None otherwise, and
coname is the name of a continue tag (e.g. else)
or None otherwise
"""
tag, end, name, args = match_ob.group(0, 'end', 'name', 'args')
args=args.strip()
if end:
if not command or name != command.name:
raise ParseError, ('unexpected end tag', tag)
return tag, args, None, None
if command and name in command.blockContinuations:
if name=='else' and args:
# Waaaaaah! Have to special case else because of
# old else start tag usage. Waaaaaaah!
l=len(args)
if not (args==sargs or
args==sargs[:l] and sargs[l:l+1] in ' \t\n'):
return tag, args, self.commands[name], None
return tag, args, None, name
try: return tag, args, self.commands[name], None
except KeyError:
raise ParseError, ('Unexpected tag', tag)
SubTemplate__roles__=()
def SubTemplate(self, name): return HTML('', __name__=name)
varExtra__roles__=()
def varExtra(self, match_ob): return 's'
manage_edit__roles__=()
def manage_edit(self,data,REQUEST=None):
'edit a template'
self.munge(data)
if REQUEST: return self.editConfirmation(self,REQUEST)
quotedHTML__roles__=()
def quotedHTML(self,
text=None,
character_entities=(
(('&'), '&amp;'),
(("<"), '&lt;' ),
((">"), '&gt;' ),
(('"'), '&quot;'))): #"
if text is None: text=self.read_raw()
for re,name in character_entities:
if text.find(re) >= 0: text=name.join(text.split(re))
return text
errQuote__roles__=()
errQuote=quotedHTML
def __str__(self):
return self.quotedHTML()
# these should probably all be deprecated.
management_interface__roles__=()
def management_interface(self):
'''Hook to allow public execution of management interface with
everything else private.'''
return self
manage_editForm__roles__=()
def manage_editForm(self, URL1, REQUEST):
'''Display doc template editing form''' #"
return self._manage_editForm(
self,
mapping=REQUEST,
__str__=str(self),
URL1=URL1
)
manage_editDocument__roles__=()
manage__roles__=()
manage_editDocument=manage=manage_editForm
class HTMLDefault(HTML):
'''\
HTML document templates that edit themselves through copy.
This is to make a distinction from HTML objects that should edit
themselves in place.
'''
copy_class__roles__=()
copy_class=HTML
manage_edit__roles__=()
def manage_edit(self,data,PARENTS,URL1,REQUEST):
'edit a template'
newHTML=self.copy_class(data,self.globals,self.__name__)
setattr(PARENTS[1],URL1[URL1.rfind('/')+1:],newHTML)
return self.editConfirmation(self,REQUEST)
class HTMLFile(FileMixin, HTML):
"""\
HTML Document templates read from files.
If the object is pickled, the file name, rather
than the file contents is pickled. When the object is
unpickled, then the file will be re-read to obtain the string.
Note that the file will not be read until the document
template is used the first time.
"""
manage_default__roles__=()
def manage_default(self, REQUEST=None):
'Revert to factory defaults'
if self.edited_source:
self.edited_source=''
self._v_cooked=self.cook()
if REQUEST: return self.editConfirmation(self,REQUEST)
manage_editForm__roles__=()
def manage_editForm(self, URL1, REQUEST):
'''Display doc template editing form'''
return self._manage_editForm(mapping=REQUEST,
document_template_edit_width=
self.document_template_edit_width,
document_template_edit_header=
self.document_template_edit_header,
document_template_form_header=
self.document_template_form_header,
document_template_edit_footer=
self.document_template_edit_footer,
URL1=URL1,
__str__=str(self),
FactoryDefaultString=FactoryDefaultString,
)
manage_editDocument__roles__=()
manage__roles__=()
manage_editDocument=manage=manage_editForm
manage_edit__roles__=()
def manage_edit(self,data,
PARENTS=[],URL1='',URL2='',REQUEST='', SUBMIT=''):
'edit a template'
if SUBMIT==FactoryDefaultString: return self.manage_default(REQUEST)
if data.find('\r'):
data='\n\r'.join(data.split('\r\n'))
data='\n'.join(data.split('\n\r'))
if self.edited_source:
self.edited_source=data
self._v_cooked=self.cook()
else:
__traceback_info__=self.__class__
newHTML=self.__class__()
newHTML.__setstate__(self.__getstate__())
newHTML.edited_source=data
setattr(PARENTS[1],URL1[URL1.rfind('/')+1:],newHTML)
if REQUEST: return self.editConfirmation(self,REQUEST)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''Conditional insertion
Conditional insertion is performed using 'if' and 'else'
commands.
To include text when an object is true using the EPFS
format, use::
%(if name)[
text
%(if name)]
To include text when an object is true using the HTML
format, use::
<!--#if name-->
text
<!--#/if name-->
where 'name' is the name bound to the object.
To include text when an object is false using the EPFS
format, use::
%(else name)[
text
%(else name)]
To include text when an object is false using the HTML
format, use::
<!--#else name-->
text
<!--#/else name-->
Finally to include text when an object is true and to
include different text when the object is false using the
EPFS format, use::
%(if name)[
true text
%(if name)]
%(else name)[
false text
%(else name)]
and to include text when an object is true and to
include different text when the object is false using the
HTML format, use::
<!--#if name-->
true text
<!--#else name-->
false text
<!--#/if name-->
Notes:
- if a variable is nor defined, it is considered to be false.
- A variable if only evaluated once in an 'if' tag. If the value
is used inside the tag, including in enclosed tags, the
variable is not reevaluated.
'''
__rcs_id__='$Id$'
__version__='$Revision: 1.19 $'[11:-2]
from DocumentTemplate.DT_Util import ParseError, parse_params, name_param, str
class If:
blockContinuations='else','elif'
name='if'
elses=None
expr=''
def __init__(self, blocks):
tname, args, section = blocks[0]
args=parse_params(args, name='', expr='')
name,expr=name_param(args,'if',1)
self.__name__= name
if expr is None: cond=name
else: cond=expr.eval
sections=[cond, section.blocks]
if blocks[-1][0]=='else':
tname, args, section = blocks[-1]
del blocks[-1]
args=parse_params(args, name='')
if args:
ename,expr=name_param(args,'else',1)
if ename != name:
raise ParseError, ('name in else does not match if', 'in')
elses=section.blocks
else: elses=None
for tname, args, section in blocks[1:]:
if tname=='else':
raise ParseError, (
'more than one else tag for a single if tag', 'in')
args=parse_params(args, name='', expr='')
name,expr=name_param(args,'elif',1)
if expr is None: cond=name
else: cond=expr.eval
sections.append(cond)
sections.append(section.blocks)
if elses is not None: sections.append(elses)
self.simple_form=('i',)+tuple(sections)
class Unless:
name='unless'
blockContinuations=()
def __init__(self, blocks):
tname, args, section = blocks[0]
args=parse_params(args, name='', expr='')
name,expr=name_param(args,'unless',1)
if expr is None: cond=name
else: cond=expr.eval
self.simple_form=('i',cond,None,section.blocks)
class Else(Unless):
# The else tag is included for backward compatibility and is deprecated.
name='else'
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Sequence insertion
A sequence may be inserted using an 'in' command. The 'in'
command specifies the name of a sequence object and text to
be inserted for each element in the sequence.
The EPFS syntax for the in command is::
%(in name)[
text
%(in name)]
The HTML syntax for the in command is::
<!--#in name-->
text
<!--#/in name-->
See the example below that shows how 'if', 'else', and 'in' commands
may be combined to display a possibly empty list of objects.
The text included within an 'in' command will be refered to
as an 'in' block.
Synopsis
If the variable 'sequence' exists as a sequence, a simple case
of the 'in' tag is used as follows::
<!--#in sequence-->some markup<!--#/in-->
A more complete case is used as follows::
<!--#in sequence sort=age-->
<!--#var sequence-number-->) <!--#var age-->
<!--#/in-->
Attributes
sort -- Define the sort order for sequence items. Parameter to the
attribute is either a sort option, or list of sort options separated
by comma. Every sort option consists of variable name, optional
comparison function name (default is cmp) and optional sort order
(default is asc).
Examples: sort="date" or sort="date,time" or
sort="title/locale,date/cmp/desc". If you want to specify sort order,
you cannot omit the function; use cmp for standard comparison.
Few predefined comparison functions available: standard cmp,
nocase (ignore string case), strcoll (alias "locale"),
strcoll_nocase (alias "locale_nocase"). Locale functions are
available only if module locale is already imported (you started Zope
with -L locale).
sort_expr -- The "sort" attribute accepts only static list of
sort options. This calculated parameter allows you to calculate the
list of sort options on the fly.
sort_expr -- This allows an expression to control sort order.
reverse -- Reverse the sequence (may be combined with sort). Note
that this can cause a huge memory use in lazy activation instances.
reverse_expr -- This calculated parameter allows you to calculate the
need of reversing on the fly.
Within an 'in' block, variables are substituted from the
elements of the iteration unless the 'no_push_item' optional
is specified. The elements may be either instance or mapping
objects. In addition, the variables:
'sequence-item' -- The element.
'sequence-var-nnn' -- The value of a specific named attribute
of the item, where 'nnn' is the name. For example, to get
an items 'title' attribute, use 'sequence-var-title'. This
construct is most useful in an 'if' tag to test whether an
attribute is present, because the attribute lookup will be
extended to the full document template namespace.
'sequence-key' -- The key associated with the element in an
items list. See below.
'sequence-index' -- The index, starting from 0, of the
element within the sequence.
'sequence-number' -- The index, starting from 1, of the
element within the sequence.
'sequence-letter' -- The index, starting from 'a', of the
element within the sequence.
'sequence-Letter' -- The index, starting from 'A', of the
element within the sequence.
'sequence-roman' -- The index, starting from 'i', of the
element within the sequence.
'sequence-Roman' -- The index, starting from 'I', of the
element within the sequence.
'sequence-start' -- A variable that is true if the element
being displayed is the first of the displayed elements,
and false otherwise.
'sequence-end' -- A variable that is true if the element
being displayed is the last of the displayed elements,
and false otherwise.
are defined for each element.
Normally, 'in' blocks are used to iterate over sequences of
instances. If the optional parameter 'mapping' is specified
after the sequence name, then the elements of the sequence
will be treated as mapping objects.
An 'in' command may be used to iterate over a sequence of
dictionary items. If the elements of the iteration are
two-element tuples, then then the template code given in the
'in' block will be applied to the second element of each
tuple and may use a variable, 'sequence-key' to access the
first element in each tuple.
Batch sequence insertion
When displaying a large number of objects, it is sometimes
desirable to display just a sub-sequence of the data.
An 'in' command may have optional parameters,
as in::
<!--#in values start=start_var size=7-->
The parameter values may be either integer literals or
variable names.
Up to five parameters may be set:
'start' -- The number of the first element to be shown,
where elements are numbered from 1.
'end' -- The number of the last element to be shown,
where elements are numbered from 1.
'size' -- The desired number of elements to be shown at
once.
'orphan' -- The desired minimum number of objects to be
displayed. The default value for this
parameter is 0.
'overlap' -- The desired overlap between batches. The
default is no overlap.
Typically, only 'start' and 'size' will be specified.
When batch insertion is used, several additional variables are
defined for use within the sequence insertion text:
'sequence-query' -- The original query string given in a get
request with the form variable named in the 'start'
attribute removed. This is extremely useful when
building URLs to fetch another batch.
To see how this is used, consider the following example::
<!--#in search_results size=20 start=batch_start-->
... display rows
<!--#if sequence-end--> <!--#if next-sequence-->
<a href="&dtml-URL;/&dtml-sequence-query;batch_start=&dtml-next-sequence-start-number;">
(Next &dtml-next-sequence-size; results)
</a>
<!--#/if--> <!--#/if-->
<!--#/in-->
If the original URL is: 'foo/bar?x=1&y=2', then the
rendered text (after row data are displayed) will be::
<a href="foo/bar?x=1&amp;y=2&amp;batch_start=20">
(Next 20 results)
</a>
If the original URL is: 'foo/bar?batch_start=10&x=1&y=2',
then the rendered text (after row data are displayed)
will be::
<a href="foo/bar?x=1&amp;y=2&amp;batch_start=30">
(Next 20 results)
</a>
'sequence-step-start-index' -- The index, starting from 0,
of the start of the current batch.
'sequence-step-end-index' -- The index, starting from 0, of
the end of the current batch.
'sequence-step-size' -- The batch size used.
'previous-sequence' -- This variable will be true when the
first element is displayed and when the first element
displayed is not the first element in the sequence.
'previous-sequence-start-index' -- The index, starting from
0, of the start of the batch previous to the current
batch.
'previous-sequence-end-index' -- The index, starting from
0, of the end of the batch previous to the current
batch.
'previous-sequence-size' -- The size of the batch previous to
the current batch.
'previous-batches' -- A sequence of mapping objects
containing information about all of the batches prior
to the batch being displayed.
Each of these mapping objects include the following
variables:
batch-start-index -- The index, starting from
0, of the beginning of the batch.
batch-end-index -- The index, starting from
0, of the end of the batch.
batch-size -- The size of the batch.
'next-sequence' -- This variable will be true when the last
element is displayed and when the last element
displayed is not the last element in the sequence.
'next-sequence-start-index' -- The index, starting from
0, of the start of the batch after the current
batch.
'next-sequence-end-index' -- The index, starting from
0, of the end of the batch after the current
batch.
'next-sequence-size' -- The size of the batch after
the current batch.
'next-batches' -- A sequence of mapping objects
containing information about all of the batches after
the batch being displayed.
Each of these mapping objects include the following
variables:
batch-start-index -- The index, starting from
0, of the beginning of the batch.
batch-end-index -- The index, starting from
0, of the end of the batch.
batch-size -- The size of the batch.
For each of the variables listed above with names ending in
"-index", there are variables with names ending in "-number",
"-roman", "-Roman", "-letter", and "-Letter" that are indexed
from 1, "i", "I", "a", and "A", respectively. In addition,
for every one of these variables there are variables with
names ending in "-var-xxx", where "xxx" is an element
attribute name or key.
Summary statistics
When performing sequence insertion, special variables may be
used to obtain summary statistics. To obtain a summary
statistic for a variable, use the variable name:
'statistic-name', where 'statistic' is a statistic name and
'name' is the name of a data variable.
Currently supported statistic names are:
total -- The total of numeric values.
count -- The total number of non-missing values.
min -- The minimum of non-missing values.
max -- The maximum of non-missing values.
median -- The median of non-missing values.
mean -- The mean of numeric values values.
variance -- The variance of numeric values computed with a
degrees of freedom equal to the count - 1.
variance-n -- The variance of numeric values computed with a
degrees of freedom equal to the count.
standard-deviation -- The standard deviation of numeric values
computed with a degrees of freedom equal to the count - 1.
standard-deviation-n -- The standard deviation of numeric
values computed with a degrees of freedom equal to the count.
Missing values are either 'None' or the attribute 'Value'
of the module 'Missing', if present.
'else' continuation tag within in
An 'else' tag may be used as a continuation tag in the 'in' tag.
The source after the 'else' tag is inserted if:
- The sequence given to the 'in' tag is of zero length, or
- The 'previous' attribute was used and their are no
previous batches, or
- The 'next' attribute was used and their are no
next batches, or
''' #'
__rcs_id__='$Id$'
__version__='$Revision: 1.62 $'[11:-2]
import sys
import re
from DocumentTemplate.DT_Util import ParseError, parse_params, name_param
from DocumentTemplate.DT_Util import str, join_unicode
from DocumentTemplate.DT_Util import render_blocks, InstanceDict
from DocumentTemplate.DT_Util import ValidationError, Eval
from DocumentTemplate.DT_Util import simple_name, add_with_prefix
from DocumentTemplate.DT_InSV import sequence_variables, opt
TupleType = tuple
StringTypes = (str, unicode)
class InFactory:
blockContinuations=('else',)
name='in'
def __call__(self, blocks):
i=InClass(blocks)
if i.batch: return i.renderwb
else: return i.renderwob
In=InFactory()
class InClass:
elses=None
expr=sort=batch=mapping=no_push_item=None
start_name_re=None
reverse=None
sort_expr=reverse_expr=None
def __init__(self, blocks):
tname, args, section = blocks[0]
args=parse_params(args, name='', start='1',end='-1',size='10',
orphan='0',overlap='1',mapping=1,
no_push_item=1,
skip_unauthorized=1,
previous=1, next=1, expr='', sort='',
reverse=1, sort_expr='', reverse_expr='',
prefix='')
self.args=args
has_key=args.has_key
if has_key('sort'):
self.sort=sort=args['sort']
if sort=='sequence-item': self.sort=''
if has_key('sort_expr'):
self.sort_expr=Eval(args['sort_expr'])
if has_key('reverse_expr'):
self.reverse_expr=Eval(args['reverse_expr'])
if has_key('reverse'):
self.reverse=args['reverse']
if has_key('no_push_item'):
self.no_push_item=args['no_push_item']
if has_key('mapping'): self.mapping=args['mapping']
for n in 'start', 'size', 'end':
if has_key(n): self.batch=1
prefix = args.get('prefix')
if prefix and not simple_name(prefix):
raise ParseError, _tm(
'prefix is not a simple name', 'in')
for n in 'orphan','overlap','previous','next':
if has_key(n) and not self.batch:
raise ParseError, (
"""
The %s attribute was used but neither of the
<code>start</code>, <code>end</code>, or <code>size</code>
attributes were used.
""" % n, 'in')
if has_key('start'):
v=args['start']
if type(v)==type(''):
try: int(v)
except:
self.start_name_re=re.compile(
'&+'+
''.join(["[%s]" % c for c in v])+
'=[0-9]+&+')
name,expr=name_param(args,'in',1)
if expr is not None: expr=expr.eval
self.__name__, self.expr = name, expr
self.section=section.blocks
if len(blocks) > 1:
if len(blocks) != 2: raise ParseError, (
'too many else blocks', 'in')
tname, args, section = blocks[1]
args=parse_params(args, name='')
if args:
ename=name_param(args)
if ename != name:
raise ParseError, (
'name in else does not match in', 'in')
self.elses=section.blocks
def renderwb(self, md):
expr=self.expr
name=self.__name__
if expr is None:
sequence=md[name]
cache={ name: sequence }
else:
sequence=expr(md)
cache=None
if not sequence:
if self.elses: return render_blocks(self.elses, md)
return ''
if type(sequence) is type(''):
raise ValueError, (
'Strings are not allowed as input to the in tag.')
section=self.section
params=self.args
mapping=self.mapping
no_push_item=self.no_push_item
if self.sort_expr is not None:
self.sort=self.sort_expr.eval(md)
sequence=self.sort_sequence(sequence, md)
elif self.sort is not None:
sequence=self.sort_sequence(sequence, md)
if self.reverse_expr is not None and self.reverse_expr.eval(md):
sequence=self.reverse_sequence(sequence)
elif self.reverse is not None:
sequence=self.reverse_sequence(sequence)
next=previous=0
try: start=int_param(params,md,'start',0)
except: start=1
end=int_param(params,md,'end',0)
size=int_param(params,md,'size',0)
overlap=int_param(params,md,'overlap',0)
orphan=int_param(params,md,'orphan','0')
start,end,sz=opt(start,end,size,orphan,sequence)
if params.has_key('next'): next=1
if params.has_key('previous'): previous=1
last=end-1
first=start-1
try: query_string=md['QUERY_STRING']
except: query_string=''
prefix = params.get('prefix')
vars = sequence_variables(sequence, '?'+query_string,
self.start_name_re, prefix)
kw=vars.data
pkw = add_with_prefix(kw, 'sequence', prefix)
for k, v in kw.items():
pkw[k] = v
pkw['sequence-step-size']=sz
pkw['sequence-step-overlap']=overlap
pkw['sequence-step-start']=start
pkw['sequence-step-end']=end
pkw['sequence-step-start-index']=start-1
pkw['sequence-step-end-index']=end-1
pkw['sequence-step-orphan']=orphan
kw['mapping']=mapping
push=md._push
pop=md._pop
render=render_blocks
if cache: push(cache)
push(vars)
try:
if previous:
if first > 0:
pstart,pend,psize=opt(0,first+overlap,
sz,orphan,sequence)
pkw['previous-sequence']=1
pkw['previous-sequence-start-index']=pstart-1
pkw['previous-sequence-end-index']=pend-1
pkw['previous-sequence-size']=pend+1-pstart
result=render(section,md)
elif self.elses: result=render(self.elses, md)
else: result=''
elif next:
try:
# The following line is a sneaky way to test whether
# there are more items, without actually
# computing a length:
sequence[end]
except IndexError:
if self.elses: result=render(self.elses, md)
else: result=''
else:
pstart,pend,psize=opt(end+1-overlap,0,
sz,orphan,sequence)
pkw['next-sequence']=1
pkw['next-sequence-start-index']=pstart-1
pkw['next-sequence-end-index']=pend-1
pkw['next-sequence-size']=pend+1-pstart
result=render(section,md)
else:
result = []
append=result.append
guarded_getitem = getattr(md, 'guarded_getitem', None)
for index in range(first,end):
# preset
pkw['previous-sequence']= 0
pkw['next-sequence']= 0 # now more often defined then previously
#
if index==first or index==last:
# provide batching information
if first > 0:
pstart,pend,psize=opt(0,first+overlap,
sz,orphan,sequence)
if index==first: pkw['previous-sequence']=1
pkw['previous-sequence-start-index']=pstart-1
pkw['previous-sequence-end-index']=pend-1
pkw['previous-sequence-size']=pend+1-pstart
try:
# The following line is a sneaky way to
# test whether there are more items,
# without actually computing a length:
sequence[end]
pstart,pend,psize=opt(end+1-overlap,0,
sz,orphan,sequence)
if index==last: pkw['next-sequence']=1
pkw['next-sequence-start-index']=pstart-1
pkw['next-sequence-end-index']=pend-1
pkw['next-sequence-size']=pend+1-pstart
except: pass
if index==last: pkw['sequence-end']=1
if guarded_getitem is not None:
try: client = guarded_getitem(sequence, index)
except ValidationError, vv:
if (params.has_key('skip_unauthorized') and
params['skip_unauthorized']):
if index==first: pkw['sequence-start']=0
continue
raise ValidationError, '(item %s): %s' % (
index, vv), sys.exc_info()[2]
else:
client = sequence[index]
pkw['sequence-index']=index
t = type(client)
if t is TupleType and len(client)==2:
client=client[1]
if no_push_item:
pushed = 0
elif mapping:
pushed = 1
push(client)
elif t in StringTypes:
pushed = 0
else:
pushed = 1
push(InstanceDict(client, md))
try: append(render(section, md))
finally:
if pushed:
pop()
if index==first: pkw['sequence-start']=0
result = join_unicode(result)
finally:
if cache: pop()
pop()
return result
def renderwob(self, md):
"""RENDER WithOutBatch"""
expr=self.expr
name=self.__name__
if expr is None:
sequence=md[name]
cache={ name: sequence }
else:
sequence=expr(md)
cache=None
if not sequence:
if self.elses: return render_blocks(self.elses, md)
return ''
if type(sequence) is type(''):
raise ValueError, (
'Strings are not allowed as input to the in tag.')
section=self.section
mapping=self.mapping
no_push_item=self.no_push_item
if self.sort_expr is not None:
self.sort=self.sort_expr.eval(md)
sequence=self.sort_sequence(sequence, md)
elif self.sort is not None:
sequence=self.sort_sequence(sequence, md)
if self.reverse_expr is not None and self.reverse_expr.eval(md):
sequence=self.reverse_sequence(sequence)
elif self.reverse is not None:
sequence=self.reverse_sequence(sequence)
prefix = self.args.get('prefix')
vars=sequence_variables(sequence, alt_prefix=prefix)
kw=vars.data
pkw = add_with_prefix(kw, 'sequence', prefix)
for k, v in kw.items():
pkw[k] = v
kw['mapping']=mapping
l=len(sequence)
last=l-1
push=md._push
pop=md._pop
render=render_blocks
if cache: push(cache)
push(vars)
try:
result = []
append=result.append
guarded_getitem = getattr(md, 'guarded_getitem', None)
for index in range(l):
if index==last: pkw['sequence-end']=1
if guarded_getitem is not None:
try: client = guarded_getitem(sequence, index)
except ValidationError, vv:
if (self.args.has_key('skip_unauthorized') and
self.args['skip_unauthorized']):
if index==1: pkw['sequence-start']=0
continue
raise ValidationError, '(item %s): %s' % (
index, vv), sys.exc_info()[2]
else:
client = sequence[index]
pkw['sequence-index']=index
t = type(client)
if t is TupleType and len(client)==2:
client=client[1]
if no_push_item:
pushed = 0
elif mapping:
pushed = 1
push(client)
elif t in StringTypes:
pushed = 0
else:
pushed = 1
push(InstanceDict(client, md))
try: append(render(section, md))
finally:
if pushed:
pop()
if index==0: pkw['sequence-start']=0
result = join_unicode(result)
finally:
if cache: pop()
pop()
return result
def sort_sequence(self, sequence, md):
# Modified with multiple sort fields by Ross Lazarus
# April 7 2000 rossl@med.usyd.edu.au
# eg <dtml-in "foo" sort="akey,anotherkey">
# Modified with advanced sort functions by
# Oleg Broytmann <phd@phd.pp.ru> 30 Mar 2001
# eg <dtml-in "foo" sort="akey/nocase,anotherkey/cmp/desc">
sort=self.sort
need_sortfunc = sort.find('/') >= 0
sortfields = sort.split(',') # multi sort = key1,key2
multsort = len(sortfields) > 1 # flag: is multiple sort
if need_sortfunc:
# prepare the list of functions and sort order multipliers
sf_list = make_sortfunctions(sortfields, md)
# clean the mess a bit
if multsort: # More than one sort key.
sortfields = map(lambda x: x[0], sf_list)
else:
sort = sf_list[0][0]
mapping=self.mapping
isort=not sort
s=[]
for client in sequence:
k = None
if type(client)==TupleType and len(client)==2:
if isort: k=client[0]
v=client[1]
else:
if isort: k=client
v=client
if sort:
if multsort: # More than one sort key.
k = []
for sk in sortfields:
if mapping: akey = v.get(sk)
else: akey = getattr(v, sk, None)
if not basic_type(akey):
try: akey = akey()
except: pass
k.append(akey)
else: # One sort key.
if mapping: k = v.get(sort)
else: k = getattr(v, sort, None)
if not basic_type(type(k)):
try: k = k()
except: pass
s.append((k,client))
if need_sortfunc:
by = SortBy(multsort, sf_list)
s.sort(by)
else:
s.sort()
sequence=[]
for k, client in s:
sequence.append(client)
return sequence
def reverse_sequence(self, sequence):
s=list(sequence)
s.reverse()
return s
basic_type={type(''): 1, type(0): 1, type(0.0): 1, type(()): 1, type([]): 1,
type(None) : 1 }.has_key
def int_param(params,md,name,default=0, st=type('')):
try: v=params[name]
except: v=default
if v:
try: v=int(v)
except:
v=md[v]
if type(v) is st: v=int(v)
return v
# phd: Advanced sort support
def nocase(str1, str2):
return cmp(str1.lower(), str2.lower())
if sys.modules.has_key("locale"): # only if locale is already imported
from locale import strcoll
def strcoll_nocase(str1, str2):
return strcoll(str1.lower(), str2.lower())
def make_sortfunctions(sortfields, md):
"""Accepts a list of sort fields; splits every field, finds comparison
function. Returns a list of 3-tuples (field, cmp_function, asc_multplier)"""
sf_list = []
for field in sortfields:
f = field.split('/')
l = len(f)
if l == 1:
f.append("cmp")
f.append("asc")
elif l == 2:
f.append("asc")
elif l == 3:
pass
else:
raise SyntaxError, "sort option must contain no more than 2 slashes"
f_name = f[1]
# predefined function?
if f_name == "cmp":
func = cmp # builtin
elif f_name == "nocase":
func = nocase
elif f_name in ("locale", "strcoll"):
func = strcoll
elif f_name in ("locale_nocase", "strcoll_nocase"):
func = strcoll_nocase
else: # no - look it up in the namespace
func = md.getitem(f_name, 0)
sort_order = f[2].lower()
if sort_order == "asc":
multiplier = +1
elif sort_order == "desc":
multiplier = -1
else:
raise SyntaxError, "sort oder must be either ASC or DESC"
sf_list.append((f[0], func, multiplier))
return sf_list
class SortBy:
def __init__(self, multsort, sf_list):
self.multsort = multsort
self.sf_list = sf_list
def __call__(self, o1, o2):
multsort = self.multsort
if multsort:
o1 = o1[0] # if multsort - take the first element (key list)
o2 = o2[0]
sf_list = self.sf_list
l = len(sf_list)
# assert that o1 and o2 are tuples of apropriate length
assert len(o1) == l + 1 - multsort, "%s, %d" % (o1, l + multsort)
assert len(o2) == l + 1 - multsort, "%s, %d" % (o2, l + multsort)
# now run through the list of functions in sf_list and
# compare every object in o1 and o2
for i in range(l):
# if multsort - we already extracted the key list
# if not multsort - i is 0, and the 0th element is the key
c1, c2 = o1[i], o2[i]
func, multiplier = sf_list[i][1:3]
n = func(c1, c2)
if n: return n*multiplier
# all functions returned 0 - identical sequences
return 0
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''Sequence variables support
$Id$'''
__version__='$Revision: 1.22 $'[11:-2]
from math import sqrt
import re
try:
import Missing
mv=Missing.Value
except: mv=None
TupleType = tuple
class sequence_variables:
alt_prefix = None
def __init__(self,items=None,query_string='',start_name_re=None,
alt_prefix=''):
self.items=items
self.query_string=query_string
self.start_name_re=start_name_re
if alt_prefix:
self.alt_prefix = alt_prefix + '_'
self.data=data={
'previous-sequence': 0,
'next-sequence': 0,
'sequence-start': 1,
'sequence-end': 0,
}
def __len__(self): return 1
def number(self,index): return index+1
def even(self,index): return index%2 == 0
def odd(self,index): return index%2
def letter(self,index): return chr(ord('a')+index)
def Letter(self,index): return chr(ord('A')+index)
def key(self,index): return self.items[index][0]
def item(self,index, tt=type(())):
i=self.items[index]
if type(i) is tt and len(i)==2: return i[1]
return i
def roman(self,index): return self.Roman(index).lower()
def Roman(self,num):
# Force number to be an integer value
num = int(num)+1
# Initialize roman as an empty string
roman = ''
while num >= 1000:
num = num - 1000
roman = '%sM' % roman
while num >= 500:
num = num - 500
roman = '%sD' % roman
while num >= 100:
num = num - 100
roman = '%sC' % roman
while num >= 50:
num = num - 50
roman = '%sL' % roman
while num >= 10:
num = num - 10
roman = '%sX' % roman
while num >= 5:
num = num - 5
roman = '%sV' % roman
while num < 5 and num >= 1:
num = num - 1
roman = '%sI' % roman
# Replaces special cases in Roman Numerals
roman = roman.replace('DCCCC', 'CM')
roman = roman.replace('CCCC', 'CD')
roman = roman.replace('LXXXX', 'XC')
roman = roman.replace('XXXX', 'XL')
roman = roman.replace('VIIII', 'IX')
roman = roman.replace('IIII', 'IV')
return roman
def value(self,index,name):
data=self.data
item=self.items[index]
if type(item)==TupleType and len(item)==2:
item=item[1]
if data['mapping']: return item[name]
return getattr(item,name)
def first(self,name,key=''):
data=self.data
if data['sequence-start']: return 1
index=data['sequence-index']
return self.value(index,name) != self.value(index-1,name)
def last(self,name,key=''):
data=self.data
if data['sequence-end']: return 1
index=data['sequence-index']
return self.value(index,name) != self.value(index+1,name)
def length(self, ignored):
l=self['sequence-length']=len(self.items)
return l
def query(self, *ignored):
if self.start_name_re is None: raise KeyError, 'sequence-query'
query_string=self.query_string
while query_string and query_string[:1] in '?&':
query_string=query_string[1:]
while query_string[-1:] == '&':
query_string=query_string[:-1]
if query_string:
query_string='&%s&' % query_string
reg=self.start_name_re
if type(reg)==type(re.compile(r"")):
mo = reg.search(query_string)
if mo is not None:
v = mo.group(0)
l = mo.start(0)
query_string=(query_string[:l]+ query_string[l+len(v)-1:])
else:
l=reg.search_group(query_string, (0,))
if l:
v=l[1]
l=l[0]
query_string=(query_string[:l]+ query_string[l+len(v)-1:])
query_string='?'+query_string[1:]
else: query_string='?'
self['sequence-query']=query_string
return query_string
statistic_names=(
'total', 'count', 'min', 'max', 'median', 'mean',
'variance', 'variance-n','standard-deviation', 'standard-deviation-n',
)
def statistics(self,name,key):
items=self.items
data=self.data
mapping=data['mapping']
count=sum=sumsq=0
min=max=None
scount=smin=smax=None
values=[]
svalues=[]
for item in items:
try:
if mapping: item=item[name]
else:
try: item=getattr(item,name)
except:
if name != 'item':
raise
try:
if item is mv:
item = None
if type(item)==type(1):
s=item*long(item)
else:
s=item*item
sum=sum+item
sumsq=sumsq+s
values.append(item)
if min is None:
min=max=item
else:
if item < min: min=item
if item > max: max=item
except:
if item is not None and item is not mv:
if smin is None: smin=smax=item
else:
if item < smin: smin=item
if item > smax: smax=item
svalues.append(item)
except: pass
# Initialize all stats to empty strings:
for stat in self.statistic_names: data['%s-%s' % (stat,name)]=''
count=len(values)
try: # Numeric statistics
n=float(count)
mean=sum/n
sumsq=sumsq/n - mean*mean
data['mean-%s' % name]=mean
data['total-%s' % name]=sum
data['variance-n-%s' % name]=sumsq
data['standard-deviation-n-%s' % name]=sqrt(sumsq)
if count > 1:
sumsq=sumsq*n/(n-1)
data['variance-%s' % name]=sumsq
data['standard-deviation-%s' % name]=sqrt(sumsq)
else:
data['variance-%s' % name]=''
data['standard-deviation-%s' % name]=''
except:
if min is None: min,max,values=smin,smax,svalues
else:
if smin < min: min=smin
if smax > max: max=smax
values=values+svalues
count=len(values)
data['count-%s' % name]=count
# data['_values']=values
if min is not None:
data['min-%s' % name]=min
data['max-%s' % name]=max
values.sort()
if count==1:
data['median-%s' % name]=min
else:
n=count+1
if n/2*2==n: data['median-%s' % name]=values[n/2-1]
else:
n=n/2
try: data['median-%s' % name]=(values[n]+values[n-1])/2
except:
try: data['median-%s' % name]=(
"between %s and %s" % (values[n],values[n-1]))
except: pass
return data[key]
def next_batches(self, suffix='batches',key=''):
if suffix != 'batches': raise KeyError, key
data=self.data
sequence=self.items
try:
if not data['next-sequence']: return ()
sz=data['sequence-step-size']
start=data['sequence-step-start']
end=data['sequence-step-end']
l=len(sequence)
orphan=data['sequence-step-orphan']
overlap=data['sequence-step-overlap']
except: AttributeError, 'next-batches'
r=[]
while end < l:
start,end,spam=opt(end+1-overlap,0,sz,orphan,sequence)
v=sequence_variables(self.items,
self.query_string,self.start_name_re)
d=v.data
d['batch-start-index']=start-1
d['batch-end-index']=end-1
d['batch-size']=end+1-start
d['mapping']=data['mapping']
r.append(v)
data['next-batches']=r
return r
def previous_batches(self, suffix='batches',key=''):
if suffix != 'batches': raise KeyError, key
data=self.data
sequence=self.items
try:
if not data['previous-sequence']: return ()
sz=data['sequence-step-size']
start=data['sequence-step-start']
end=data['sequence-step-end']
l=len(sequence)
orphan=data['sequence-step-orphan']
overlap=data['sequence-step-overlap']
except: AttributeError, 'previous-batches'
r=[]
while start > 1:
start,end,spam=opt(0,start-1+overlap,sz,orphan,sequence)
v=sequence_variables(self.items,
self.query_string,self.start_name_re)
d=v.data
d['batch-start-index']=start-1
d['batch-end-index']=end-1
d['batch-size']=end+1-start
d['mapping']=data['mapping']
r.append(v)
r.reverse()
data['previous-batches']=r
return r
special_prefixes={
'first': first,
'last': last,
'previous': previous_batches,
'next': next_batches,
# These two are for backward compatability with a missfeature:
'sequence-index': lambda self, suffix, key: self['sequence-'+suffix],
'sequence-index-is': lambda self, suffix, key: self['sequence-'+suffix],
}
for n in statistic_names: special_prefixes[n]=statistics
def __setitem__(self, key, value):
self.data[key] = value
if self.alt_prefix:
if key.startswith('sequence-'): key = key[9:]
self.data[self.alt_prefix + key] = value
def __getitem__(self,key,
special_prefixes=special_prefixes,
special_prefix=special_prefixes.has_key
):
data=self.data
if data.has_key(key): return data[key]
l=key.rfind('-')
if l < 0:
alt_prefix = self.alt_prefix
if not (alt_prefix and key.startswith(alt_prefix)):
raise KeyError, key
suffix = key[len(alt_prefix):].replace('_', '-')
if '-' in suffix:
try: return self[suffix]
except KeyError: pass
prefix = 'sequence'
key = 'sequence-' + suffix
else:
suffix=key[l+1:]
prefix=key[:l]
if hasattr(self, suffix):
try: v=data[prefix+'-index']
except: pass
else: return getattr(self,suffix)(v)
if special_prefix(prefix):
return special_prefixes[prefix](self, suffix, key)
if prefix[-4:]=='-var':
prefix=prefix[:-4]
try: return self.value(data[prefix+'-index'],suffix)
except: pass
if key=='sequence-query': return self.query()
raise KeyError, key
def opt(start,end,size,orphan,sequence):
if size < 1:
if start > 0 and end > 0 and end >= start:
size=end+1-start
else: size=7
if start > 0:
try: sequence[start-1]
except: start=len(sequence)
# if start > l: start=l
if end > 0:
if end < start: end=start
else:
end=start+size-1
try: sequence[end+orphan-1]
except: end=len(sequence)
# if l - end < orphan: end=l
elif end > 0:
try: sequence[end-1]
except: end=len(sequence)
# if end > l: end=l
start=end+1-size
if start - 1 < orphan: start=1
else:
start=1
end=start+size-1
try: sequence[end+orphan-1]
except: end=len(sequence)
# if l - end < orphan: end=l
return start,end,size
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
''' The Let tag was contributed to Zope by and is copyright, 1999
Phillip J. Eby. Permission has been granted to release the Let tag
under the Zope Public License.
Let name=value...
The 'let' tag is used to bind variables to values within a block.
The text enclosed in the let tag is rendered using information
from the given variables or expressions.
For example::
<!--#let foofunc="foo()" my_bar=bar-->
foo() = <!--#var foofunc-->,
bar = <!--#var my_bar-->
<!--#/let-->
Notice that both 'name' and 'expr' style attributes may be used to
specify data. 'name' style attributes (e.g. my_bar=bar) will be
rendered as they are for var/with/in/etc. Quoted attributes will
be treated as Python expressions.
Variables are processed in sequence, so later assignments can
reference and/or overwrite the results of previous assignments,
as desired.
'''
import re
from DocumentTemplate.DT_Util import render_blocks, Eval, ParseError
from DocumentTemplate.DT_Util import str # Probably needed due to
# hysterical pickles.
class Let:
blockContinuations=()
name='let'
def __init__(self, blocks):
tname, args, section = blocks[0]
self.__name__ = args
self.section = section.blocks
self.args = args = parse_let_params(args)
for i in range(len(args)):
name,expr = args[i]
if expr[:1]=='"' and expr[-1:]=='"' and len(expr) > 1:
# expr shorthand
expr=expr[1:-1]
try: args[i] = name, Eval(expr).eval
except SyntaxError, v:
m,(huh,l,c,src) = v
raise ParseError, (
'<strong>Expression (Python) Syntax error</strong>:'
'\n<pre>\n%s\n</pre>\n' % v[0],
'let')
def render(self, md):
d={}; md._push(d)
try:
for name,expr in self.args:
if type(expr) is type(''): d[name]=md[expr]
else: d[name]=expr(md)
return render_blocks(self.section, md)
finally: md._pop(1)
__call__ = render
def parse_let_params(text,
result=None,
tag='let',
parmre=re.compile('([\000- ]*([^\000- ="]+)=([^\000- ="]+))'),
qparmre=re.compile('([\000- ]*([^\000- ="]+)="([^"]*)")'),
**parms):
result=result or []
mo = parmre.match(text)
mo1= qparmre.match(text)
if mo is not None:
name=mo.group(2)
value=mo.group(3)
l=len(mo.group(1))
elif mo1 is not None:
name=mo1.group(2)
value='"%s"' % mo1.group(3)
l=len(mo1.group(1))
else:
if not text or not text.strip(): return result
raise ParseError, ('invalid parameter: "%s"' % text, tag)
result.append((name,value))
text=text[l:].strip()
if text: return parse_let_params(text,result,tag,**parms)
else: return result
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Raising exceptions
Errors can be raised from DTML using the 'raise' tag.
For example::
<!--#if expr="condition_that_tests_input"-->
<!--#raise type="Input Error"-->
The value you entered is not valid
<!--#/raise-->
<!--#/if-->
'''
__rcs_id__='$Id$'
__version__='$Revision: 1.13 $'[11:-2]
from zExceptions import upgradeException
from zExceptions import convertExceptionType
from DocumentTemplate.DT_Util import name_param
from DocumentTemplate.DT_Util import parse_params
from DocumentTemplate.DT_Util import render_blocks
class InvalidErrorTypeExpression(Exception):
pass
class Raise:
blockContinuations=()
name='raise'
expr=''
def __init__(self, blocks):
tname, args, section = blocks[0]
self.section = section.blocks
args = parse_params(args, type='', expr='')
self.__name__, self.expr = name_param(args, 'raise', 1, attr='type')
def render(self,md):
expr = self.expr
if expr is None:
t = convertExceptionType(self.__name__)
if t is None:
t = RuntimeError
else:
try:
t = expr.eval(md)
except:
t = convertExceptionType(self.__name__)
if t is None:
t = InvalidErrorTypeExpression
try:
v = render_blocks(self.section, md)
except:
v = 'Invalid Error Value'
# String Exceptions are deprecated on Python 2.5 and
# plain won't work at all on Python 2.6. So try to upgrade it
# to a real exception.
t, v = upgradeException(t, v)
raise t, v
__call__ = render
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__version__='$Revision: 1.9 $'[11:-2]
from DocumentTemplate.DT_Util import parse_params, name_param
class ReturnTag:
name='return'
expr=None
def __init__(self, args):
args = parse_params(args, name='', expr='')
name, expr = name_param(args, 'var', 1)
self.__name__ = name
self.expr = expr
def render(self, md):
if self.expr is None:
val = md[self.__name__]
else:
val = self.expr.eval(md)
raise DTReturn(val)
__call__ = render
class DTReturn:
def __init__(self, v):
self.v = v
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"$Id$"
import os
import thread
import re
from DocumentTemplate.DT_Util import ParseError, InstanceDict
from DocumentTemplate.DT_Util import TemplateDict, render_blocks, str
from DocumentTemplate.DT_Var import Var, Call, Comment
from DocumentTemplate.DT_Return import ReturnTag, DTReturn
_marker = [] # Create a new marker object.
class String:
"""Document templates defined from strings.
Document template strings use an extended form of python string
formatting. To insert a named value, simply include text of the
form: '%(name)x', where 'name' is the name of the value and 'x' is
a format specification, such as '12.2d'.
To intrduce a block such as an 'if' or an 'in' or a block continuation,
such as an 'else', use '[' as the format specification. To
terminate a block, ise ']' as the format specification, as in::
%(in results)[
%(name)s
%(in results)]
"""
isDocTemp=1
# Document Templates masquerade as functions:
class func_code: pass
func_code=func_code()
func_code.co_varnames='self','REQUEST'
func_code.co_argcount=2
func_code.__roles__=()
func_defaults__roles__=()
func_defaults=()
errQuote__roles__=()
def errQuote(self, s): return s
parse_error__roles__=()
def parse_error(self, mess, tag, text, start):
raise ParseError, "%s, for tag %s, on line %s of %s" % (
mess, self.errQuote(tag), len(text[:start].split('\n')),
self.errQuote(self.__name__))
commands__roles__=()
commands={
'var': Var,
'call': Call,
'in': ('in', 'DT_In','In'),
'with': ('with', 'DT_With','With'),
'if': ('if', 'DT_If','If'),
'unless': ('unless', 'DT_If','Unless'),
'else': ('else', 'DT_If','Else'),
'comment': Comment,
'raise': ('raise', 'DT_Raise','Raise'),
'try': ('try', 'DT_Try','Try'),
'let': ('let', 'DT_Let', 'Let'),
'return': ReturnTag,
}
SubTemplate__roles__=()
def SubTemplate(self, name):
return String('', __name__=name)
tagre__roles__=()
def tagre(self):
return re.compile(
'%\\(' # beginning
'(?P<name>[a-zA-Z0-9_/.-]+)' # tag name
'('
'[\000- ]+' # space after tag name
'(?P<args>([^\\)"]+("[^"]*")?)*)' # arguments
')?'
'\\)(?P<fmt>[0-9]*[.]?[0-9]*[a-z]|[]![])' # end
, re.I)
_parseTag__roles__=()
def _parseTag(self, match_ob, command=None, sargs='', tt=type(())):
tag, args, command, coname = self.parseTag(match_ob,command,sargs)
if type(command) is tt:
cname, module, name = command
d={}
try:
exec 'from %s import %s' % (module, name) in d
except ImportError:
exec 'from DocumentTemplate.%s import %s' % (module, name) in d
command=d[name]
self.commands[cname]=command
return tag, args, command, coname
parseTag__roles__=()
def parseTag(self, match_ob, command=None, sargs=''):
"""Parse a tag using an already matched re
Return: tag, args, command, coname
where: tag is the tag,
args is the tag\'s argument string,
command is a corresponding command info structure if the
tag is a start tag, or None otherwise, and
coname is the name of a continue tag (e.g. else)
or None otherwise
"""
tag, name, args, fmt = match_ob.group(0, 'name', 'args', 'fmt')
args=args and args.strip() or ''
if fmt==']':
if not command or name != command.name:
raise ParseError, ('unexpected end tag', tag)
return tag, args, None, None
elif fmt=='[' or fmt=='!':
if command and name in command.blockContinuations:
if name=='else' and args:
# Waaaaaah! Have to special case else because of
# old else start tag usage. Waaaaaaah!
l=len(args)
if not (args==sargs or
args==sargs[:l] and sargs[l:l+1] in ' \t\n'):
return tag, args, self.commands[name], None
return tag, args, None, name
try: return tag, args, self.commands[name], None
except KeyError:
raise ParseError, ('Unexpected tag', tag)
else:
# Var command
args=args and ("%s %s" % (name, args)) or name
return tag, args, Var, None
varExtra__roles__=()
def varExtra(self, match_ob):
return match_ob.group('fmt')
parse__roles__=()
def parse(self,text,start=0,result=None,tagre=None):
if result is None: result=[]
if tagre is None: tagre=self.tagre()
mo = tagre.search(text,start)
while mo :
l = mo.start(0)
try: tag, args, command, coname = self._parseTag(mo)
except ParseError, m: self.parse_error(m[0],m[1],text,l)
s=text[start:l]
if s: result.append(s)
start=l+len(tag)
if hasattr(command,'blockContinuations'):
start=self.parse_block(text, start, result, tagre,
tag, l, args, command)
else:
try:
if command is Var: r=command(args, self.varExtra(mo))
else: r=command(args)
if hasattr(r,'simple_form'): r=r.simple_form
result.append(r)
except ParseError, m: self.parse_error(m[0],tag,text,l)
mo = tagre.search(text,start)
text=text[start:]
if text: result.append(text)
return result
skip_eol__roles__=()
def skip_eol(self, text, start, eol=re.compile('[ \t]*\n')):
# if block open is followed by newline, then skip past newline
mo =eol.match(text,start)
if mo is not None:
start = start + mo.end(0) - mo.start(0)
return start
parse_block__roles__=()
def parse_block(self, text, start, result, tagre,
stag, sloc, sargs, scommand):
start=self.skip_eol(text,start)
blocks=[]
tname=scommand.name
sname=stag
sstart=start
sa=sargs
while 1:
mo = tagre.search(text,start)
if mo is None: self.parse_error('No closing tag', stag, text, sloc)
l = mo.start(0)
try: tag, args, command, coname= self._parseTag(mo,scommand,sa)
except ParseError, m: self.parse_error(m[0],m[1], text, l)
if command:
start=l+len(tag)
if hasattr(command, 'blockContinuations'):
# New open tag. Need to find closing tag.
start=self.parse_close(text, start, tagre, tag, l,
command, args)
else:
# Either a continuation tag or an end tag
section=self.SubTemplate(sname)
section._v_blocks=section.blocks=self.parse(text[:l],sstart)
section._v_cooked=None
blocks.append((tname,sargs,section))
start=self.skip_eol(text,l+len(tag))
if coname:
tname=coname
sname=tag
sargs=args
sstart=start
else:
try:
r=scommand(blocks)
if hasattr(r,'simple_form'): r=r.simple_form
result.append(r)
except ParseError, m: self.parse_error(m[0],stag,text,l)
return start
parse_close__roles__=()
def parse_close(self, text, start, tagre, stag, sloc, scommand, sa):
while 1:
mo = tagre.search(text,start)
if mo is None: self.parse_error('No closing tag', stag, text, sloc)
l = mo.start(0)
try: tag, args, command, coname= self._parseTag(mo,scommand,sa)
except ParseError, m: self.parse_error(m[0],m[1], text, l)
start=l+len(tag)
if command:
if hasattr(command, 'blockContinuations'):
# New open tag. Need to find closing tag.
start=self.parse_close(text, start, tagre, tag, l,
command,args)
elif not coname: return start
shared_globals__roles__=()
shared_globals={}
def __init__(self, source_string='', mapping=None, __name__='<string>',
**vars):
"""\
Create a document template from a string.
The optional parameter, 'mapping', may be used to provide a
mapping object containing defaults for values to be inserted.
"""
self.raw=source_string
self.initvars(mapping, vars)
self.setName(__name__)
def name(self): return self.__name__
id=name
setName__roles__=()
def setName(self,v): self.__dict__['__name__']=v
default__roles__=()
def default(self,name=None,**kw):
"""\
Change or query default values in a document template.
If a name is specified, the value of the named default value
before the operation is returned.
Keyword arguments are used to provide default values.
"""
if name: name=self.globals[name]
for key in kw.keys(): self.globals[key]=kw[key]
return name
var__roles__=()
def var(self,name=None,**kw):
"""\
Change or query a variable in a document template.
If a name is specified, the value of the named variable before
the operation is returned.
Keyword arguments are used to provide variable values.
"""
if name: name=self._vars[name]
for key in kw.keys(): self._vars[key]=kw[key]
return name
munge__roles__=()
def munge(self,source_string=None,mapping=None,**vars):
"""\
Change the text or default values for a document template.
"""
if mapping is not None or vars:
self.initvars(mapping, vars)
if source_string is not None:
self.raw=source_string
self.cook()
manage_edit__roles__=()
def manage_edit(self,data,REQUEST=None):
self.munge(data)
read_raw__roles__=()
def read_raw(self,raw=None):
return self.raw
read__roles__=()
def read(self,raw=None):
return self.read_raw()
cook__roles__=()
def cook(self,
cooklock=thread.allocate_lock(),
):
cooklock.acquire()
try:
self._v_blocks=self.parse(self.read())
self._v_cooked=None
finally:
cooklock.release()
initvars__roles__=()
def initvars(self, globals, vars):
if globals:
for k in globals.keys():
if k[:1] != '_' and not vars.has_key(k): vars[k]=globals[k]
self.globals=vars
self._vars={}
ZDocumentTemplate_beforeRender__roles__ = ()
def ZDocumentTemplate_beforeRender(self, md, default):
return default
ZDocumentTemplate_afterRender__roles__ = ()
def ZDocumentTemplate_afterRender(self, md, result):
pass
def __call__(self,client=None,mapping={},**kw):
'''\
Generate a document from a document template.
The document will be generated by inserting values into the
format string specified when the document template was
created. Values are inserted using standard python named
string formats.
The optional argument 'client' is used to specify a object
containing values to be looked up. Values will be looked up
using getattr, so inheritence of values is supported. Note
that names beginning with '_' will not be looked up from the
client.
The optional argument, 'mapping' is used to specify a mapping
object containing values to be inserted.
Values to be inserted may also be specified using keyword
arguments.
Values will be inserted from one of several sources. The
sources, in the order in which they are consulted, are:
o Keyword arguments,
o The 'client' argument,
o The 'mapping' argument,
o The keyword arguments provided when the object was
created, and
o The 'mapping' argument provided when the template was
created.
'''
# print '============================================================'
# print '__called__'
# print self.raw
# print kw
# print client
# print mapping
# print '============================================================'
if mapping is None: mapping = {}
if hasattr(mapping, 'taintWrapper'): mapping = mapping.taintWrapper()
if not hasattr(self,'_v_cooked'):
try: changed=self.__changed__()
except: changed=1
self.cook()
if not changed: self.__changed__(0)
pushed=None
try:
# Support Python 1.5.2, but work better in 2.1
if (mapping.__class__ is TemplateDict or
isinstance(mapping, TemplateDict)): pushed=0
except: pass
globals=self.globals
if pushed is not None:
# We were passed a TemplateDict, so we must be a sub-template
md=mapping
push=md._push
if globals:
push(self.globals)
pushed=pushed+1
else:
md=TemplateDict()
push=md._push
shared_globals=self.shared_globals
if shared_globals: push(shared_globals)
if globals: push(globals)
if mapping:
push(mapping)
md.guarded_getattr=self.guarded_getattr
md.guarded_getitem=self.guarded_getitem
if client is not None:
if type(client)==type(()):
md.this=client[-1]
else: md.this=client
pushed=0
level=md.level
if level > 200: raise SystemError, (
'infinite recursion in document template')
md.level=level+1
if client is not None:
if type(client)==type(()):
# if client is a tuple, it represents a "path" of clients
# which should be pushed onto the md in order.
for ob in client:
push(InstanceDict(ob, md)) # Circ. Ref. 8-|
pushed=pushed+1
else:
# otherwise its just a normal client object.
push(InstanceDict(client, md)) # Circ. Ref. 8-|
pushed=pushed+1
if self._vars:
push(self._vars)
pushed=pushed+1
if kw:
push(kw)
pushed=pushed+1
try:
value = self.ZDocumentTemplate_beforeRender(md, _marker)
if value is _marker:
try: result = render_blocks(self._v_blocks, md)
except DTReturn, v: result = v.v
self.ZDocumentTemplate_afterRender(md, result)
return result
else:
return value
finally:
if pushed: md._pop(pushed) # Get rid of circular reference!
md.level=level # Restore previous level
guarded_getattr=None
guarded_getitem=None
def __str__(self):
return self.read()
def __getstate__(self, _special=('_v_', '_p_')):
# Waaa, we need _v_ behavior but we may not subclass Persistent
d={}
for k, v in self.__dict__.items():
if k[:3] in _special: continue
d[k]=v
return d
class FileMixin:
# Mix-in class to abstract certain file-related attributes
edited_source=''
def __init__(self, file_name='', mapping=None, __name__='', **vars):
"""\
Create a document template based on a named file.
The optional parameter, 'mapping', may be used to provide a
mapping object containing defaults for values to be inserted.
"""
self.raw=file_name
self.initvars(mapping, vars)
self.setName(__name__ or file_name)
read_raw__roles__=()
def read_raw(self):
if self.edited_source: return self.edited_source
if not os.path.exists(self.raw):
print 'file not found: %s' % self.raw
if self.raw: return open(self.raw,'r').read()
return ''
class File(FileMixin, String):
"""\
Document templates read from files.
If the object is pickled, the file name, rather
than the file contents is pickled. When the object is
unpickled, then the file will be re-read to obtain the string.
Note that the file will not be read until the document
template is used the first time.
"""
manage_edit__roles__=()
def manage_edit(self,data): raise TypeError, 'cannot edit files'
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import sys, traceback
from cStringIO import StringIO
from DocumentTemplate.DT_Util import ParseError, parse_params, render_blocks
from DocumentTemplate.DT_Util import namespace, InstanceDict
from DocumentTemplate.DT_Return import DTReturn
class Try:
"""Zope DTML Exception handling
usage:
<!--#try-->
<!--#except SomeError AnotherError-->
<!--#except YetAnotherError-->
<!--#except-->
<!--#else-->
<!--#/try-->
or:
<!--#try-->
<!--#finally-->
<!--#/try-->
The DTML try tag functions quite like Python's try command.
The contents of the try tag are rendered. If an exception is raised,
then control switches to the except blocks. The first except block to
match the type of the error raised is rendered. If an except block has
no name then it matches all raised errors.
The try tag understands class-based exceptions, as well as string-based
exceptions. Note: the 'raise' tag raises string-based exceptions.
Inside the except blocks information about the error is available via
three variables.
'error_type' -- This variable is the name of the exception caught.
'error_value' -- This is the caught exception's value.
'error_tb' -- This is a traceback for the caught exception.
The optional else block is rendered when no exception occurs in the
try block. Exceptions in the else block are not handled by the preceding
except blocks.
The try..finally form specifies a `cleanup` block, to be rendered even
when an exception occurs. Note that any rendered result is discarded if
an exception occurs in either the try or finally blocks. The finally block
is only of any use if you need to clean up something that will not be
cleaned up by the transaction abort code.
The finally block will always be called, wether there was an exception in
the try block or not, or wether or not you used a return tag in the try
block. Note that any output of the finally block is discarded if you use a
return tag in the try block.
If an exception occurs in the try block, and an exception occurs in the
finally block, or you use the return tag in that block, any information
about that first exception is lost. No information about the first
exception is available in the finally block. Also, if you use a return tag
in the try block, and an exception occurs in the finally block or you use
a return tag there as well, the result returned in the try block will be
lost.
Original version by Jordan B. Baker.
Try..finally and try..else implementation by Martijn Pieters.
"""
name = 'try'
blockContinuations = 'except', 'else', 'finally'
finallyBlock=None
elseBlock=None
def __init__(self, blocks):
tname, args, section = blocks[0]
self.args = parse_params(args)
self.section = section.blocks
# Find out if this is a try..finally type
if len(blocks) == 2 and blocks[1][0] == 'finally':
self.finallyBlock = blocks[1][2].blocks
# This is a try [except]* [else] block.
else:
# store handlers as tuples (name,block)
self.handlers = []
defaultHandlerFound = 0
for tname,nargs,nsection in blocks[1:]:
if tname == 'else':
if not self.elseBlock is None:
raise ParseError, (
'No more than one else block is allowed',
self.name)
self.elseBlock = nsection.blocks
elif tname == 'finally':
raise ParseError, (
'A try..finally combination cannot contain '
'any other else, except or finally blocks',
self.name)
else:
if not self.elseBlock is None:
raise ParseError, (
'The else block should be the last block '
'in a try tag', self.name)
for errname in nargs.split():
self.handlers.append((errname,nsection.blocks))
if nargs.strip()=='':
if defaultHandlerFound:
raise ParseError, (
'Only one default exception handler '
'is allowed', self.name)
else:
defaultHandlerFound = 1
self.handlers.append(('',nsection.blocks))
def render(self, md):
if (self.finallyBlock is None):
return self.render_try_except(md)
else:
return self.render_try_finally(md)
def render_try_except(self, md):
result = ''
# first we try to render the first block
try:
result = render_blocks(self.section, md)
except DTReturn:
raise
except:
# but an error occurs.. save the info.
t,v = sys.exc_info()[:2]
if type(t)==type(''):
errname = t
else:
errname = t.__name__
handler = self.find_handler(t)
if handler is None:
# we didn't find a handler, so reraise the error
raise
# found the handler block, now render it
try:
f=StringIO()
traceback.print_exc(100,f)
error_tb=f.getvalue()
ns = namespace(md, error_type=errname, error_value=v,
error_tb=error_tb)[0]
md._push(InstanceDict(ns,md))
return render_blocks(handler, md)
finally:
md._pop(1)
else:
# No errors have occured, render the optional else block
if (self.elseBlock is None):
return result
else:
return result + render_blocks(self.elseBlock, md)
def render_try_finally(self, md):
result = ''
# first try to render the first block
try:
result = render_blocks(self.section, md)
# Then handle finally block
finally:
result = result + render_blocks(self.finallyBlock, md)
return result
def find_handler(self,exception):
"recursively search for a handler for a given exception"
if type(exception)==type(''):
for e,h in self.handlers:
if exception==e or e=='':
return h
else:
return None
for e,h in self.handlers:
if e==exception.__name__ or e=='' or self.match_base(exception,e):
return h
return None
def match_base(self,exception,name):
for base in exception.__bases__:
if base.__name__==name or self.match_base(base,name):
return 1
return None
__call__ = render
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''Machinery to support through-the-web editing
$Id$'''
__version__='$Revision: 1.15 $'[11:-2]
from DocumentTemplate.DT_HTML import HTML
FactoryDefaultString="Factory Default"
HTML.document_template_edit_header='<h2>Edit Document</h2>'
HTML.document_template_form_header=''
HTML.document_template_edit_footer=(
"""<FONT SIZE="-1">
<I><A HREF="http://www.zope.com">
&copy; 2002 Zope Corporation</A></I></FONT>""")
HTML._manage_editForm = HTML(
"""<HTML>
<HEAD>
<TITLE>HTML Template Editor</TITLE>
</HEAD>
<BODY bgcolor="#FFFFFF">
<!--#var document_template_edit_header-->
<FORM name="editform" ACTION="&dtml-URL1;/manage_edit" METHOD="POST">
<!--#var document_template_form_header-->
Document template source:
<center>
<br>
<dtml-let cols="REQUEST.get('dtpref_cols', '100%')"
rows="REQUEST.get('dtpref_rows', '20')">
<dtml-if expr="cols[-1]=='%'">
<textarea name="data:text" style="width: &dtml-cols;;"
<dtml-else>
<textarea name="data:text" cols="&dtml-cols;"
</dtml-if>
rows="&dtml-rows;"><dtml-var __str__></textarea>
</dtml-let>
<br>
<INPUT NAME=SUBMIT TYPE="SUBMIT" VALUE="Change">
<INPUT NAME=SUBMIT TYPE="RESET" VALUE="Reset">
<INPUT NAME="dt_edit_name" TYPE="HIDDEN"
VALUE="&dtml-URL1;">
<!--#if FactoryDefaultString-->
<INPUT NAME=SUBMIT TYPE="SUBMIT"
VALUE="&dtml-FactoryDefaultString;">
<!--#/if FactoryDefaultString-->
<INPUT NAME=SUBMIT TYPE="SUBMIT" VALUE="Cancel">
<!--#if HTTP_REFERER-->
<INPUT NAME="CANCEL_ACTION" TYPE="HIDDEN"
VALUE="&dtml-HTTP_REFERER;">
<!--#else HTTP_REFERER-->
<!--#if URL1-->
<INPUT NAME="CANCEL_ACTION" TYPE="HIDDEN"
VALUE="&dtml-URL1;">
<!--#/if URL1-->
<!--#/if HTTP_REFERER-->
</center>
</FORM>
<BR CLEAR="ALL">
<!--#var document_template_edit_footer-->
</BODY>
</HTML>""",)
HTML.editConfirmation=HTML(
"""<html><head><title>Change Successful</title></head><body>
<!--#if CANCEL_ACTION-->
<form action="&dtml-CANCEL_ACTION;" method="POST">
<center>
<em>&dtml-dt_edit_name;</em><br>has been changed.<br><br>
<input type=submit name="SUBMIT" value="OK">
</center>
</form></body></html>
<!--#else CANCEL_ACTION-->
<center>
<em>&dtml-dt_edit_name;</em><br>has been changed.
</center>
<!--#/if CANCEL_ACTION-->""")
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""DTML Utilities
$Id$
"""
import re
import string
from types import BuiltinFunctionType
from types import FunctionType
from AccessControl.tainted import TaintedString
from AccessControl.ZopeGuards import _safe_globals
from RestrictedPython.Guards import safe_builtins
from RestrictedPython.Utilities import utility_builtins
from RestrictedPython.Eval import RestrictionCapableEval
# for import by other modules, dont remove!
from DocumentTemplate.html_quote import html_quote, ustr
from DocumentTemplate.cDocumentTemplate import InstanceDict, TemplateDict
from DocumentTemplate.cDocumentTemplate import render_blocks, safe_callable
from DocumentTemplate.cDocumentTemplate import join_unicode
from DocumentTemplate import sequence
if 'test' not in utility_builtins:
from RestrictedPython.Utilities import test
utility_builtins['test'] = test
test = utility_builtins['test'] # for backwards compatibility, dont remove!
utility_builtins['sequence'] = sequence
safe_builtins['sequence'] = sequence
_safe_globals['sequence'] = sequence
LIMITED_BUILTINS = 1
str=__builtins__['str'] # Waaaaa, waaaaaaaa needed for pickling waaaaa
class ParseError(Exception):
"""Document Template Parse Error"""
from zExceptions import Unauthorized as ValidationError
def int_param(params,md,name,default=0, st=type('')):
v = params.get(name, default)
if v:
try:
v = int(v)
except:
v = md[v]
if isinstance(v, str):
v = int(v)
return v or 0
functype = type(int_param)
class NotBindable:
# Used to prevent TemplateDict from trying to bind to functions.
def __init__(self, f):
self.__call__ = f
for name, f in safe_builtins.items() + utility_builtins.items():
if type(f) is functype:
f = NotBindable(f)
setattr(TemplateDict, name, f)
if LIMITED_BUILTINS:
# Replace certain builtins with limited versions.
from RestrictedPython.Limits import limited_builtins
for name, f in limited_builtins.items():
if type(f) is functype:
f = NotBindable(f)
setattr(TemplateDict, name, f)
# Wrap the string module so it can deal with TaintedString strings.
class StringModuleWrapper:
def __getattr__(self, key):
attr = getattr(string, key)
if (isinstance(attr, FunctionType) or
isinstance(attr, BuiltinFunctionType)):
return StringFunctionWrapper(attr)
else:
return attr
class StringFunctionWrapper:
def __init__(self, method):
self._method = method
def __call__(self, *args, **kw):
tainted = 0
args = list(args)
for i in range(len(args)):
if isinstance(args[i], TaintedString):
tainted = 1
args[i] = str(args[i])
for k, v in kw.items():
if isinstance(v, TaintedString):
tainted = 1
kw[k] = str(v)
args = tuple(args)
retval = self._method(*args, **kw)
if tainted and isinstance(retval, str) and '<' in retval:
retval = TaintedString(retval)
return retval
TemplateDict.string = StringModuleWrapper()
TemplateDict.__allow_access_to_unprotected_subobjects__ = 1
# The functions below are meant to bind to the TemplateDict.
_marker = [] # Create a new marker object.
def careful_getattr(md, inst, name, default=_marker):
get = md.guarded_getattr
if get is None:
get = getattr
try:
return get(inst, name)
except AttributeError:
if default is _marker:
raise
return default
def careful_hasattr(md, inst, name):
get = md.guarded_getattr
if get is None:
get = getattr
try:
get(inst, name)
except (AttributeError, ValidationError, KeyError):
return 0
else:
return 1
TemplateDict.getattr = careful_getattr
TemplateDict.hasattr = careful_hasattr
def namespace(self, **kw):
"""Create a tuple consisting of a single instance whose attributes are
provided as keyword arguments."""
if not (getattr(self, '__class__', None) == TemplateDict or
isinstance(self, TemplateDict)):
raise TypeError,'''A call was made to DT_Util.namespace() with an
incorrect "self" argument. It could be caused by a product which
is not yet compatible with this version of Zope. The traceback
information may contain more details.)'''
return self(**kw)
TemplateDict.namespace = namespace
def render(self, v):
"Render an object in the way done by the 'name' attribute"
if hasattr(v, '__render_with_namespace__'):
v = v.__render_with_namespace__(self)
else:
vbase = getattr(v, 'aq_base', v)
if safe_callable(vbase):
if getattr(vbase, 'isDocTemp', 0):
v = v(None, self)
else:
v = v()
return v
TemplateDict.render = render
class Eval(RestrictionCapableEval):
def eval(self, md):
gattr = getattr(md, 'guarded_getattr', None)
if gattr is not None:
gitem = getattr(md, 'guarded_getitem', None)
self.prepRestrictedCode()
code = self.rcode
d = {'_': md, '_vars': md,
'_getattr_': gattr,
'_getitem_': gitem,
'__builtins__': None}
else:
self.prepUnrestrictedCode()
code = self.ucode
d = {'_': md, '_vars': md}
d.update(self.globals)
has_key = d.has_key
for name in self.used:
__traceback_info__ = name
try:
if not has_key(name):
d[name] = md.getitem(name, 0)
except KeyError:
# Swallow KeyErrors since the expression
# might not actually need the name. If it
# does need the name, a NameError will occur.
pass
return eval(code, d)
def __call__(self, **kw):
# Never used?
md = TemplateDict()
md._push(kw)
return self.eval(md)
simple_name = re.compile('[a-z][a-z0-9_]*', re.I).match
class Add_with_prefix:
def __init__(self, map, defprefix, prefix):
self.map = map
self.defprefix = defprefix
self.prefix = prefix
def __setitem__(self, name, value):
map = self.map
map[name] = value
dp = self.defprefix
if name.startswith(dp + '-'):
map[self.prefix + name[len(dp):].replace('-', '_')] = value
else:
map['%s_%s' % (self.prefix, name)] = value
def add_with_prefix(map, defprefix, prefix):
if not prefix: return map
return Add_with_prefix(map, defprefix, prefix)
def name_param(params,tag='',expr=0, attr='name', default_unnamed=1):
used=params.has_key
__traceback_info__=params, tag, expr, attr
#if expr and used('expr') and used('') and not used(params['']):
# # Fix up something like: <!--#in expr="whatever" mapping-->
# params[params['']]=default_unnamed
# del params['']
if used(''):
v=params['']
if v[:1]=='"' and v[-1:]=='"' and len(v) > 1: # expr shorthand
if used(attr):
raise ParseError('%s and expr given' % attr, tag)
if expr:
if used('expr'):
raise ParseError('two exprs given', tag)
v=v[1:-1]
try: expr=Eval(v)
except SyntaxError, v:
raise ParseError(
'<strong>Expression (Python) Syntax error</strong>:'
'\n<pre>\n%s\n</pre>\n' % v[0],
tag)
return v, expr
else: raise ParseError(
'The "..." shorthand for expr was used in a tag '
'that doesn\'t support expr attributes.',
tag)
else: # name shorthand
if used(attr):
raise ParseError('Two %s values were given' % attr, tag)
if expr:
if used('expr'):
# raise 'Waaaaaa', 'waaa'
raise ParseError('%s and expr given' % attr, tag)
return params[''],None
return params['']
elif used(attr):
if expr:
if used('expr'):
raise ParseError('%s and expr given' % attr, tag)
return params[attr],None
return params[attr]
elif expr and used('expr'):
name=params['expr']
expr=Eval(name)
return name, expr
raise ParseError('No %s given' % attr, tag)
Expr_doc="""
Python expression support
Several document template tags, including 'var', 'in', 'if', 'else',
and 'elif' provide support for using Python expressions via an
'expr' tag attribute.
Expressions may be used where a simple variable value is
inadequate. For example, an expression might be used to test
whether a variable is greater than some amount::
<!--#if expr="age > 18"-->
or to transform some basic data::
<!--#var expr="phone[:3]"-->
Objects available in the document templates namespace may be used.
Subobjects of these objects may be used as well, although subobject
access is restricted by the optional validation method.
In addition, a special additional name, '_', is available. The '_'
variable provides access to the document template namespace as a
mapping object. This variable can be useful for accessing objects
in a document template namespace that have names that are not legal
Python variable names::
<!--#var expr="_['sequence-number']*5"-->
This variable also has attributes that provide access to standard
utility objects. These attributes include:
- The objects: 'None', 'abs', 'chr', 'divmod', 'float', 'hash',
'hex', 'int', 'len', 'max', 'min', 'oct', 'ord', 'pow',
'round', and 'str' from the standard Python builtin module.
- Special security-aware versions of 'getattr' and 'hasattr',
- The Python 'string', 'math', and 'random' modules, and
- A special function, 'test', that supports if-then expressions.
The 'test' function accepts any number of arguments. If the
first argument is true, then the second argument is returned,
otherwise if the third argument is true, then the fourth
argument is returned, and so on. If there is an odd number of
arguments, then the last argument is returned in the case that
none of the tested arguments is true, otherwise None is
returned.
For example, to convert a value to lower case::
<!--#var expr="_.string.lower(title)"-->
"""
ListType=type([])
def parse_params(text,
result=None,
tag='',
unparmre=re.compile('([\000- ]*([^\000- ="]+))'),
qunparmre=re.compile('([\000- ]*("[^"]*"))'),
parmre=re.compile('([\000- ]*([^\000- ="]+)=([^\000- ="]+))'),
qparmre=re.compile('([\000- ]*([^\000- ="]+)="([^"]*)")'),
**parms):
"""Parse tag parameters
The format of tag parameters consists of 1 or more parameter
specifications separated by whitespace. Each specification
consists of an unnamed and unquoted value, a valueless name, or a
name-value pair. A name-value pair consists of a name and a
quoted or unquoted value separated by an '='.
The input parameter, text, gives the text to be parsed. The
keyword parameters give valid parameter names and default values.
If a specification is not a name-value pair and it is not the
first specification and it is a
valid parameter name, then it is treated as a name-value pair with
a value as given in the keyword argument. Otherwise, if it is not
a name-value pair, it is treated as an unnamed value.
The data are parsed into a dictionary mapping names to values.
Unnamed values are mapped from the name '""'. Only one value may
be given for a name and there may be only one unnamed value. """
result=result or {}
# HACK - we precalculate all matches. Maybe we don't need them
# all. This should be fixed for performance issues
mo_p = parmre.match(text)
mo_q = qparmre.match(text)
mo_unp = unparmre.match(text)
mo_unq = qunparmre.match(text)
if mo_p:
name=mo_p.group(2).lower()
value=mo_p.group(3)
l=len(mo_p.group(1))
elif mo_q:
name=mo_q.group(2).lower()
value=mo_q.group(3)
l=len(mo_q.group(1))
elif mo_unp:
name=mo_unp.group(2)
l=len(mo_unp.group(1))
if result:
if parms.has_key(name):
if parms[name] is None: raise ParseError(
'Attribute %s requires a value' % name, tag)
result[name]=parms[name]
else: raise ParseError(
'Invalid attribute name, "%s"' % name, tag)
else:
result['']=name
return parse_params(text[l:],result,**parms)
elif mo_unq:
name=mo_unq.group(2)
l=len(mo_unq.group(1))
if result: raise ParseError(
'Invalid attribute name, "%s"' % name, tag)
else: result['']=name
return parse_params(text[l:],result,**parms)
else:
if not text or not text.strip(): return result
raise ParseError('invalid parameter: "%s"' % text, tag)
if not parms.has_key(name):
raise ParseError(
'Invalid attribute name, "%s"' % name, tag)
if result.has_key(name):
p=parms[name]
if type(p) is not ListType or p:
raise ParseError(
'Duplicate values for attribute "%s"' % name, tag)
result[name]=value
text=text[l:].strip()
if text: return parse_params(text,result,**parms)
else: return result
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Variable insertion parameters
When inserting variables, parameters may be specified to
control how the data will be formatted. In HTML source, the
'fmt' parameter is used to specify a C-style or custom format
to be used when inserting an object. In EPFS source, the 'fmt'
parameter is only used for custom formats, a C-style format is
specified after the closing parenthesis.
Custom formats
A custom format is used when outputing user-defined
objects. The value of a custom format is a method name to
be invoked on the object being inserted. The method should
return an object that, when converted to a string, yields
the desired text. For example, the DTML code::
<dtml-var date fmt=DayOfWeek>
Inserts the result of calling the method 'DayOfWeek' of the
object bound to the variable 'date', with no arguments.
In addition to object methods, serveral additional custom
formats are available:
'whole-dollars' -- Show a numeric value with a dollar symbol.
'dollars-and-cents' -- Show a numeric value with a dollar
symbol and two decimal places.
'collection-length' -- Get the length of a collection of objects.
Note that when using the EPFS source format, both a
C-style and a custom format may be provided. In this case,
the C-Style format is applied to the result of calling
the custom formatting method.
Null values and missing variables
In some applications, and especially in database applications,
data variables may alternate between "good" and "null" or
"missing" values. A format that is used for good values may be
inappropriate for null values. For this reason, the 'null'
parameter can be used to specify text to be used for null
values. Null values are defined as values that:
- Cannot be formatted with the specified format, and
- Are either the special Python value 'None' or
are false and yield an empty string when converted to
a string.
For example, when showing a monitary value retrieved from a
database that is either a number or a missing value, the
following variable insertion might be used::
<dtml-var cost fmt="$%.2d" null=\'n/a\'>
Missing values are providing for variables which are not
present in the name space, rather than raising an NameError,
you could do this:
<dtml-var cost missing=0>
and in this case, if cost was missing, it would be set to 0.
In the case where you want to deal with both at the same time,
you can use 'default':
<dtml-var description default=''>
In this case, it would use '' if the value was null or if the
variable was missing.
String manipulation
A number of special attributes are provided to transform the
value after formatting has been applied. These parameters
are supplied without arguments.
'lower' -- cause all upper-case letters to be converted to lower case.
'upper' -- cause all upper-case letters to be converted to lower case.
'capitalize' -- cause the first character of the inserted value
to be converted to upper case.
'spacify' -- cause underscores in the inserted value to be
converted to spaces.
'thousands_commas' -- cause commas to be inserted every three
digits to the left of a decimal point in values containing
numbers. For example, the value, "12000 widgets" becomes
"12,000 widgets".
'html_quote' -- convert characters that have special meaning
in HTML to HTML character entities.
'url_quote' -- convert characters that have special meaning
in URLS to HTML character entities using decimal values.
'url_quote_plus' -- like url_quote but also replace blank
space characters with '+'. This is needed for building
query strings in some cases.
'url_unquote' -- convert HTML character entities in strings
back to their real values.
'url_unquote_plus' -- like url_unquote, but also
replace '+' characters with spaces.
'sql_quote' -- Convert single quotes to pairs of single
quotes. This is needed to safely include values in
Standard Query Language (SQL) strings.
'newline_to_br' -- Convert newlines and carriage-return and
newline combinations to break tags.
'url' -- Get the absolute URL of the object by calling it\'s
'absolute_url' method, if it has one.
Truncation
The attributes 'size' and 'etc' can be used to truncate long
strings. If the 'size' attribute is specified, the string to
be inserted is truncated at the given length. If a space
occurs in the second half of the truncated string, then the
string is further truncated to the right-most space. After
truncation, the value given for the 'etc' attribute is added to
the string. If the 'etc' attribute is not provided, then '...'
is used. For example, if the value of spam is
'"blah blah blah blah"', then the tag
'<dtml-var spam size=10>' inserts '"blah blah ..."'.
Evaluating expressions without rendering results
A 'call' tag is provided for evaluating named objects or expressions
without rendering the result.
"""
import logging
import re
import string
import sys
from urllib import quote, quote_plus, unquote, unquote_plus
from Acquisition import aq_base
from AccessControl.tainted import TaintedString
from zope.structuredtext.document import DocumentWithImages
# for import by other modules, dont remove!
from DocumentTemplate.html_quote import html_quote
from DocumentTemplate.DT_Util import parse_params, name_param, str, ustr
logger = logging.getLogger('DocumentTemplate')
class Var:
name='var'
expr=None
def __init__(self, args, fmt='s'):
if args[:4]=='var ': args=args[4:]
args = parse_params(args, name='', lower=1, upper=1, expr='',
capitalize=1, spacify=1, null='', fmt='s',
size=0, etc='...', thousands_commas=1,
html_quote=1, url_quote=1, sql_quote=1,
url_quote_plus=1, url_unquote=1,
url_unquote_plus=1,missing='',
newline_to_br=1, url=1)
self.args=args
self.modifiers=tuple(
map(lambda t: t[1],
filter(lambda m, args=args, used=args.has_key:
used(m[0]) and args[m[0]],
modifiers)))
name, expr = name_param(args,'var',1)
self.__name__, self.expr = name, expr
self.fmt = fmt
if len(args)==1 and fmt=='s':
if expr is None: expr=name
else: expr=expr.eval
self.simple_form=('v', expr)
elif len(args)==2 and fmt=='s' and args.has_key('html_quote'):
if expr is None: expr=name
else: expr=expr.eval
self.simple_form=('v', expr, 'h')
def render(self, md):
args=self.args
have_arg=args.has_key
name=self.__name__
val=self.expr
if val is None:
if md.has_key(name):
if have_arg('url'):
val=md.getitem(name,0)
val=val.absolute_url()
else:
val = md[name]
else:
if have_arg('missing'):
return args['missing']
else:
raise KeyError, name
else:
val=val.eval(md)
if have_arg('url'): val=val.absolute_url()
__traceback_info__=name, val, args
if have_arg('null') and not val and val != 0:
# check for null (false but not zero, including None, [], '')
return args['null']
# handle special formats defined using fmt= first
if have_arg('fmt'):
_get = getattr(md, 'guarded_getattr', None)
if _get is None:
_get = getattr
fmt=args['fmt']
if have_arg('null') and not val and val != 0:
try:
if hasattr(val, fmt):
val = _get(val, fmt)()
elif special_formats.has_key(fmt):
if fmt == 'html-quote' and \
isinstance(val, TaintedString):
# TaintedStrings will be quoted by default, don't
# double quote.
pass
else:
val = special_formats[fmt](val, name, md)
elif fmt=='': val=''
else:
if isinstance(val, TaintedString):
val = TaintedString(fmt % val)
else:
val = fmt % val
except:
t, v= sys.exc_type, sys.exc_value
if hasattr(sys, 'exc_info'): t, v = sys.exc_info()[:2]
if val is None or not str(val): return args['null']
raise t, v
else:
# We duplicate the code here to avoid exception handler
# which tends to screw up stack or leak
if hasattr(val, fmt):
val = _get(val, fmt)()
elif special_formats.has_key(fmt):
if fmt == 'html-quote' and \
isinstance(val, TaintedString):
# TaintedStrings will be quoted by default, don't
# double quote.
pass
else:
val = special_formats[fmt](val, name, md)
elif fmt=='': val=''
else:
if isinstance(val, TaintedString):
val = TaintedString(fmt % val)
else:
val = fmt % val
# finally, pump it through the actual string format...
fmt=self.fmt
if fmt=='s':
# Keep tainted strings as tainted strings here.
if not isinstance(val, TaintedString):
val=ustr(val)
else:
# Keep tainted strings as tainted strings here.
wastainted = 0
if isinstance(val, TaintedString): wastainted = 1
val = ('%'+self.fmt) % (val,)
if wastainted and '<' in val:
val = TaintedString(val)
# next, look for upper, lower, etc
for f in self.modifiers:
if f.__name__ == 'html_quote' and isinstance(val, TaintedString):
# TaintedStrings will be quoted by default, don't double quote.
continue
val=f(val)
if have_arg('size'):
size=args['size']
try: size=int(size)
except: raise ValueError,(
'''a <code>size</code> attribute was used in a <code>var</code>
tag with a non-integer value.''')
if len(val) > size:
val=val[:size]
l=val.rfind(' ')
if l > size/2:
val=val[:l+1]
if have_arg('etc'): l=args['etc']
else: l='...'
val=val+l
if isinstance(val, TaintedString):
val = val.quoted()
return val
__call__=render
class Call:
name='call'
expr=None
def __init__(self, args):
args = parse_params(args, name='', expr='')
name, expr = name_param(args,'call',1)
if expr is None: expr=name
else: expr=expr.eval
self.simple_form=('i', expr, None)
def url_quote(v, name='(Unknown name)', md={}):
return quote(str(v))
def url_quote_plus(v, name='(Unknown name)', md={}):
return quote_plus(str(v))
def url_unquote(v, name='(Unknown name)', md={}):
return unquote(str(v))
def url_unquote_plus(v, name='(Unknown name)', md={}):
return unquote_plus(str(v))
def newline_to_br(v, name='(Unknown name)', md={}):
# Unsafe data is explicitly quoted here; we don't expect this to be HTML
# quoted later on anyway.
if isinstance(v, TaintedString): v = v.quoted()
v=ustr(v)
v = v.replace('\r', '')
v = v.replace('\n', '<br />\n')
return v
def whole_dollars(v, name='(Unknown name)', md={}):
try: return "$%d" % v
except: return ''
def dollars_and_cents(v, name='(Unknown name)', md={}):
try: return "$%.2f" % v
except: return ''
def thousands_commas(v, name='(Unknown name)', md={},
thou=re.compile(
r"([0-9])([0-9][0-9][0-9]([,.]|$))").search):
v=str(v)
vl=v.split('.')
if not vl: return v
v=vl[0]
del vl[0]
if vl: s='.'+'.'.join(vl)
else: s=''
mo=thou(v)
while mo is not None:
l = mo.start(0)
v=v[:l+1]+','+v[l+1:]
mo=thou(v)
return v+s
def whole_dollars_with_commas(v, name='(Unknown name)', md={}):
try: v= "$%d" % v
except: v=''
return thousands_commas(v)
def dollars_and_cents_with_commas(v, name='(Unknown name)', md={}):
try: v= "$%.2f" % v
except: v= ''
return thousands_commas(v)
def len_format(v, name='(Unknown name)', md={}):
return str(len(v))
def len_comma(v, name='(Unknown name)', md={}):
return thousands_commas(str(len(v)))
def restructured_text(v, name='(Unknown name)', md={}):
try:
from reStructuredText import HTML
except ImportError:
logger.info('The reStructuredText package is not available, therefor '
'the DT_Var.restructured_text function returns None.')
return None
if isinstance(v, str):
txt = v
elif aq_base(v).meta_type in ['DTML Document','DTML Method']:
txt = aq_base(v).read_raw()
else:
txt = str(v)
return HTML(txt)
def structured_text(v, name='(Unknown name)', md={}):
from zope.structuredtext.html import HTML
if isinstance(v, str):
txt = v
elif aq_base(v).meta_type in ['DTML Document','DTML Method']:
txt = aq_base(v).read_raw()
else:
txt = str(v)
level = 3
try:
from App.config import getConfiguration
except ImportError:
pass
else:
level = getConfiguration().structured_text_header_level
doc = DocumentWithImages()(txt)
return HTML()(doc, level, header=False)
def sql_quote(v, name='(Unknown name)', md={}):
"""Quote single quotes in a string by doubling them.
This is needed to securely insert values into sql
string literals in templates that generate sql.
"""
if v.find("'") >= 0: return v.replace("'", "''")
return v
special_formats={
'whole-dollars': whole_dollars,
'dollars-and-cents': dollars_and_cents,
'collection-length': len_format,
'structured-text': structured_text,
'restructured-text': restructured_text,
# The rest are deprecated:
'sql-quote': sql_quote,
'html-quote': html_quote,
'url-quote': url_quote,
'url-quote-plus': url_quote_plus,
'url-unquote': url_unquote,
'url-unquote-plus': url_unquote_plus,
'multi-line': newline_to_br,
'comma-numeric': thousands_commas,
'dollars-with-commas': whole_dollars_with_commas,
'dollars-and-cents-with-commas': dollars_and_cents_with_commas,
}
def spacify(val):
if val.find('_') >= 0: val=val.replace('_', ' ')
return val
modifiers=(html_quote, url_quote, url_quote_plus, url_unquote,
url_unquote_plus, newline_to_br,
string.lower, string.upper, string.capitalize, spacify,
thousands_commas, sql_quote, url_unquote, url_unquote_plus)
modifiers=map(lambda f: (f.__name__, f), modifiers)
class Comment:
'''Comments
The 'comment' tag can be used to simply include comments
in DTML source.
For example::
<!--#comment-->
This text is not rendered.
<!--#/comment-->
'''
name='comment'
blockContinuations=()
def __init__(self, args, fmt=''): pass
def render(self, md):
return ''
__call__=render
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Nested namespace access
The 'with' tag is used to introduce nested namespaces.
The text enclosed in the with tag is rendered using information
from the given variable or expression.
For example, if the variable 'person' is bound to an object that
has attributes 'name' and 'age', then a 'with' tag like the
following can be used to access these attributes::
<!--#with person-->
<!--#var name-->,
<!--#var age-->
<!--#/with-->
Eather a 'name' or an 'expr' attribute may be used to specify data.
A 'mapping' attribute may be used to indicate that the given data
should be treated as mapping object, rather than as an object with
named attributes.
'''
__rcs_id__='$Id$'
__version__='$Revision: 1.15 $'[11:-2]
from DocumentTemplate.DT_Util import parse_params, name_param
from DocumentTemplate.DT_Util import InstanceDict, render_blocks, str
from DocumentTemplate.DT_Util import TemplateDict
class With:
blockContinuations=()
name='with'
mapping=None
only=0
def __init__(self, blocks):
tname, args, section = blocks[0]
args=parse_params(args, name='', expr='', mapping=1, only=1)
name,expr=name_param(args,'with',1)
if expr is None: expr=name
else: expr=expr.eval
self.__name__, self.expr = name, expr
self.section=section.blocks
if args.has_key('mapping') and args['mapping']: self.mapping=1
if args.has_key('only') and args['only']: self.only=1
def render(self, md):
expr=self.expr
if type(expr) is type(''): v=md[expr]
else: v=expr(md)
if not self.mapping:
if type(v) is type(()) and len(v)==1: v=v[0]
v=InstanceDict(v,md)
if self.only:
_md=md
md=TemplateDict()
if hasattr(_md, 'guarded_getattr'):
md.guarded_getattr = _md.guarded_getattr
if hasattr(_md, 'guarded_getitem'):
md.guarded_getitem = _md.guarded_getitem
md._push(v)
try: return render_blocks(self.section, md)
finally: md._pop(1)
__call__=render
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''short description
$Id$'''
__version__='$Revision: 1.8 $'[11:-2]
from DocumentTemplate import *
import sys
def test1():
print HTML('area code=<!--#var expr="phone[:3]"-->')(phone='7035551212')
def test2():
print HTML('area code=<!--#var expr="phone.number"-->')(phone='7035551212')
def test3():
print HTML('area code=<!--#var expr="phone*1000"-->')(phone='7035551212')
def test4():
h=HTML(
"""
<!--#if expr="level==1"-->
level was 1
<!--#elif expr="level==2"-->
level was 2
<!--#elif expr="level==3"-->
level was 3
<!--#else-->
level was something else
<!--#endif-->
""")
for i in range(4):
print '-' * 77
print i, h(level=i)
print '-' * 77
if __name__ == "__main__":
try: command=sys.argv[1]
except: command='main'
globals()[command]()
The let tag:
is a new tag that lets you create blocks like:
<!--#in "1,2,3,4"-->
<!--#let num=sequence-item
index=sequence-index
result="num*index"-->
<!--#var num--> * <!--#var index--> = <!--#var result-->
<!--#/let-->
<!--#/in-->
Which yields:
1 * 0 = 0
2 * 1 = 2
3 * 2 = 6
4 * 3 = 12
The #let tag works like the #with tag, but is more flexible in that
it allows you to make multiple assignments, and allows you to chain
assignments, using earlier declarations in later assignments. Notice
inthe ablove example, the 'result' variable is based on 'num' and
'index', both of which are assigned in the same #let expression.
Syntacticly, each argument to be evalulated in the head of the let
tag must be seperated by a newline. Enclosing an argument in double
quotes causes it to be evaluated by the DTML expression machinery.
Un-quoted arguments are referenced by name.
Evaluation is in sequence with the result of earlier assignments
available to later ones. Later assignments can also override earlier
ones, which can be helpful for longer step-by-step calculations. The
variables thus set are in effect for the life of the <!--#let-->
block.
# alias module for backwards compatibility
from DocumentTemplate.DT_Util import Eval
def careful_mul(env, *factors):
r = 1
for factor in factors:
r=r*factor
return r
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Document templates with fill-in fields
Document templates provide for creation of textual documents, such as
HTML pages, from template source by inserting data from python objects
and name-spaces.
When a document template is created, a collection of default values to
be inserted may be specified with a mapping object and with keyword
arguments.
A document templated may be called to create a document with values
inserted. When called, an instance, a mapping object, and keyword
arguments may be specified to provide values to be inserted. If an
instance is provided, the document template will try to look up values
in the instance using getattr, so inheritence of values is supported.
If an inserted value is a function, method, or class, then an attempt
will be made to call the object to obtain values. This allows
instance methods to be included in documents.
Document templates masquerade as functions, so the python object
publisher (Bobo) will call templates that are stored as instances of
published objects. Bobo will pass the object the template was found in
and the HTTP request object.
Two source formats are supported:
Extended Python format strings (EPFS) --
This format is based on the insertion by name format strings
of python with additional format characters, '[' and ']' to
indicate block boundaries. In addition, parameters may be
used within formats to control how insertion is done.
For example:
%%(date fmt=DayOfWeek upper)s
causes the contents of variable 'date' to be inserted using
custom format 'DayOfWeek' and with all lower case letters
converted to upper case.
HTML --
This format uses HTML server-side-include syntax with
commands for inserting text. Parameters may be included to
customize the operation of a command.
For example:
<!--#var total fmt=12.2f-->
is used to insert the variable 'total' with the C format
'12.2f'.
Document templates support conditional and sequence insertion
Document templates extend python string substitition rules with a
mechanism that allows conditional insertion of template text and that
allows sequences to be inserted with element-wise insertion of
template text.
Access Control
Document templates provide a basic level of access control by
preventing access to names beginning with an underscore.
Additional control may be provided by providing document templates
with a 'guarded_getattr' and 'guarded_getitem' method. This would
typically be done by subclassing one or more of the DocumentTemplate
classes.
If provided, the the 'guarded_getattr' method will be called when
objects are accessed as instance attributes or when they are
accessed through keyed access in an expression.
Document Templates may be created 4 ways:
DocumentTemplate.String -- Creates a document templated from a
string using an extended form of python string formatting.
DocumentTemplate.File -- Creates a document templated bound to a
named file using an extended form of python string formatting.
If the object is pickled, the file name, rather than the file
contents is pickled. When the object is unpickled, then the
file will be re-read to obtain the string. Note that the file
will not be read until the document template is used the first
time.
DocumentTemplate.HTML -- Creates a document templated from a
string using HTML server-side-include rather than
python-format-string syntax.
DocumentTemplate.HTMLFile -- Creates an HTML document template
from a named file.
'''
__version__='$Revision: 1.14 $'[11:-2]
from DocumentTemplate.DT_Raise import ParseError
from DocumentTemplate.DT_String import String, File
from DocumentTemplate.DT_HTML import HTML, HTMLFile, HTMLDefault
# import DT_UI # Install HTML editing
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Package wrapper for Document Template
This wrapper allows the (now many) document template modules to be
segregated in a separate package."""
from DocumentTemplate.DT_String import String, File
from DocumentTemplate.DT_HTML import HTML, HTMLDefault, HTMLFile
# Register the dtml-tree tag
import TreeDisplay
from DocumentTemplate import security # Side effects!
del security
/*****************************************************************************
Copyright (c) 2002 Zope Foundation and Contributors.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
static char cDocumentTemplate_module_documentation[] =
""
"\n$Id$"
;
#include "ExtensionClass/ExtensionClass.h"
static PyObject *py_isDocTemp=0, *py_blocks=0, *py_=0, *join=0, *py_acquire;
static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
static PyObject *py__proxy_roles, *py_Unauthorized;
static PyObject *py_Unauthorized_fmt, *py_guarded_getattr;
static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
static PyObject *py___class__, *html_quote, *ustr, *untaint_name;
/* ----------------------------------------------------- */
static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define UNLESS(E) if (!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
#define OBJECT(O)(((PyObject*)O))
typedef struct {
PyObject_HEAD
PyObject *inst;
PyObject *cache;
PyObject *namespace;
PyObject *guarded_getattr;
} InstanceDictobject;
staticforward PyExtensionClass InstanceDictType;
staticforward PyObject *_join_unicode(PyObject *prejoin);
static PyObject *
InstanceDict___init__(InstanceDictobject *self, PyObject *args)
{
self->guarded_getattr=NULL;
UNLESS(PyArg_ParseTuple(args, "OO|O",
&(self->inst),
&(self->namespace),
&(self->guarded_getattr)))
return NULL;
Py_INCREF(self->inst);
Py_INCREF(self->namespace);
if (self->guarded_getattr)
Py_INCREF(self->guarded_getattr);
else
UNLESS(self->guarded_getattr=PyObject_GetAttr(self->namespace,
py_guarded_getattr))
return NULL;
UNLESS(self->cache=PyDict_New()) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static struct PyMethodDef InstanceDict_methods[] = {
{"__init__", (PyCFunction)InstanceDict___init__, 1,
""},
{NULL, NULL} /* sentinel */
};
/* ---------- */
static void
InstanceDict_dealloc(InstanceDictobject *self)
{
Py_XDECREF(self->inst);
Py_XDECREF(self->cache);
Py_XDECREF(self->namespace);
Py_XDECREF(self->guarded_getattr);
Py_DECREF(self->ob_type);
PyObject_DEL(self);
}
static PyObject *
InstanceDict_getattr(InstanceDictobject *self, PyObject *name)
{
return Py_FindAttr((PyObject *)self, name);
}
static PyObject *
InstanceDict_repr(InstanceDictobject *self)
{
return PyObject_Repr(self->inst);
}
/* Code to access InstanceDict objects as mappings */
static int
InstanceDict_length( InstanceDictobject *self)
{
return 1;
}
static PyObject *
InstanceDict_subscript( InstanceDictobject *self, PyObject *key)
{
PyObject *r, *v;
char *name;
/* Try to get value from the cache */
if ((r=PyObject_GetItem(self->cache, key))) return r;
PyErr_Clear();
/* Check for __str__ */
UNLESS(name=PyString_AsString(key)) return NULL;
if (*name=='_')
{
UNLESS(strcmp(name,"__str__")==0) goto KeyError;
return PyObject_Str(self->inst);
}
if (self->guarded_getattr != Py_None) {
r = PyObject_CallFunction(self->guarded_getattr, "OO", self->inst, key);
}
else {
r = PyObject_GetAttr(self->inst, key);
}
if (!r) {
PyObject *tb;
PyErr_Fetch(&r, &v, &tb);
if (r != PyExc_AttributeError) /* || PyObject_Compare(v,key)) */
{
PyErr_Restore(r,v,tb);
return NULL;
}
Py_XDECREF(r);
Py_XDECREF(v);
Py_XDECREF(tb);
goto KeyError;
}
if (r && PyObject_SetItem(self->cache, key, r) < 0) PyErr_Clear();
return r;
KeyError:
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
static int
InstanceDict_ass_sub( InstanceDictobject *self, PyObject *v, PyObject *w)
{
PyErr_SetString(PyExc_TypeError,
"InstanceDict objects do not support item assignment");
return -1;
}
static PyMappingMethods InstanceDict_as_mapping = {
(inquiry)InstanceDict_length, /*mp_length*/
(binaryfunc)InstanceDict_subscript, /*mp_subscript*/
(objobjargproc)InstanceDict_ass_sub, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char InstanceDicttype__doc__[] =
""
;
static PyExtensionClass InstanceDictType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"InstanceDict", /*tp_name*/
sizeof(InstanceDictobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)InstanceDict_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*obsolete tp_getattr*/
(setattrfunc)0, /*obsolete tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)InstanceDict_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&InstanceDict_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)InstanceDict_getattr, /*tp_getattro*/
0, /*tp_setattro*/
/* Space for future expansion */
0L,0L,
InstanceDicttype__doc__, /* Documentation string */
METHOD_CHAIN(InstanceDict_methods)
};
typedef struct {
PyObject_HEAD
int level;
PyObject *dict;
PyObject *data;
} MM;
staticforward PyExtensionClass MMtype;
static PyObject *
MM_push(MM *self, PyObject *args)
{
PyObject *src;
UNLESS(PyArg_Parse(args, "O", &src)) return NULL;
UNLESS(-1 != PyList_Append(self->data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
MM_pop(MM *self, PyObject *args)
{
int i=1, l;
PyObject *r;
if (args) UNLESS(PyArg_Parse(args, "i", &i)) return NULL;
if ((l=PyList_Size(self->data)) < 0) return NULL;
i=l-i;
UNLESS(r=PySequence_GetItem(self->data,l-1)) return NULL;
if (PyList_SetSlice(self->data,i,l,NULL) < 0) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static PyObject *
MM__init__(MM *self, PyObject *args)
{
UNLESS(PyArg_Parse(args, "")) return NULL;
UNLESS(self->data=PyList_New(0)) return NULL;
self->dict=NULL;
self->level=0;
Py_INCREF(Py_None);
return Py_None;
}
static int
safe_PyCallable_Check(PyObject *x)
{
PyObject *klass;
if (x == NULL)
return 0;
klass = PyObject_GetAttr(x, py___class__);
if (klass) {
PyObject *call = PyObject_GetAttr(x, py___call__);
if (call) {
Py_DECREF(klass);
Py_DECREF(call);
return 1;
}
else {
PyErr_Clear();
Py_DECREF(klass);
if (PyClass_Check(x) || PyExtensionClass_Check(x))
return 1;
else
return 0;
}
}
else {
PyErr_Clear();
return PyCallable_Check(x);
}
}
static int
dtObjectIsCallable(PyObject *ob) {
PyObject *base=0;
int result=0;
/* Ensure that an object is really callable by unwrapping it */
UNLESS(base=PyObject_GetAttr(ob, py_aq_base)) {
PyErr_Clear();
return safe_PyCallable_Check(ob);
}
result=safe_PyCallable_Check(base);
Py_DECREF(base);
return result;
}
static int
dtObjectIsDocTemp(PyObject *ob) {
PyObject *base=0;
PyObject *value=0;
int result=0;
/* Ensure that 'isDocTemp' is not acquired */
UNLESS(base=PyObject_GetAttr(ob, py_aq_base)) {
PyErr_Clear();
base = ob;
Py_INCREF(base);
}
if ( (value = PyObject_GetAttr(base, py_isDocTemp)) ) {
if (PyObject_IsTrue(value)) {
result = 1;
}
Py_DECREF(value);
}
else PyErr_Clear();
Py_DECREF(base);
return result;
}
static PyObject *
MM_cget(MM *self, PyObject *key, int call)
{
long i;
PyObject *e, *rr;
UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while (--i >= 0)
{
e=PyList_GET_ITEM(self->data,i);
if (PyDict_Check(e))
{
e=PyDict_GetItem(e, key);
Py_XINCREF(e);
}
else
{
UNLESS (e=PyObject_GetItem(e,key))
{
if (PyErr_Occurred() == PyExc_KeyError)
PyErr_Clear();
else
return NULL;
}
}
if (e)
{
if (!call) return e;
/* Try calling __render_with_namespace__ */
if ((rr = PyObject_GetAttr(e, py_renderNS)))
{
Py_DECREF(e);
UNLESS_ASSIGN(rr, PyObject_CallFunction(rr, "O", self))
return NULL;
return rr;
}
else PyErr_Clear();
if (dtObjectIsCallable(e))
{
/* Try calling the object */
if (dtObjectIsDocTemp(e))
{
ASSIGN(e,PyObject_CallFunction(e,"OO", Py_None, self));
UNLESS(e) return NULL;
return e;
}
rr=PyObject_CallObject(e,NULL);
if (rr) ASSIGN(e,rr);
else {
Py_DECREF(e);
return NULL;
}
}
return e;
}
}
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
static PyObject *
MM_get(MM *self, PyObject *args)
{
PyObject *key, *call=Py_None;
UNLESS(PyArg_ParseTuple(args,"O|O",&key,&call)) return NULL;
return MM_cget(self, key, PyObject_IsTrue(call));
}
static PyObject *
MM_has_key(MM *self, PyObject *args)
{
PyObject *key;
UNLESS(PyArg_ParseTuple(args,"O",&key)) return NULL;
if ((key=MM_cget(self, key, 0)))
{
Py_DECREF(key);
return PyInt_FromLong(1);
}
PyErr_Clear();
return PyInt_FromLong(0);
}
static struct PyMethodDef MM_methods[] = {
{"__init__", (PyCFunction)MM__init__, 0,
"__init__() -- Create a new empty multi-mapping"},
{"_push", (PyCFunction) MM_push, 0,
"_push(mapping_object) -- Add a data source"},
{"_pop", (PyCFunction) MM_pop, 0,
"_pop() -- Remove and return the last data source added"},
{"getitem", (PyCFunction) MM_get, METH_VARARGS,
"getitem(key[,call]) -- Get a value from the MultiDict\n\n"
"If call is true, callable objects that can be called without arguments are\n"
"called during retrieval.\n"
"If call is false, the object will be returns without any attempt to call it.\n"
"If not specified, call is false by default.\n"
},
{"has_key", (PyCFunction) MM_has_key, METH_VARARGS,
"has_key(key) -- Test whether the mapping has the given key"
},
{NULL, NULL} /* sentinel */
};
static void
MM_dealloc(MM *self)
{
Py_XDECREF(self->data);
Py_XDECREF(self->dict);
Py_DECREF(self->ob_type);
PyObject_DEL(self);
}
static PyObject *
MM_getattro(MM *self, PyObject *name)
{
if (PyString_Check(name))
{
if (strcmp(PyString_AsString(name),"level")==0)
return PyInt_FromLong(self->level);
}
if (self->dict)
{
PyObject *v;
if ((v=PyDict_GetItem(self->dict, name)))
{
Py_INCREF(v);
return v;
}
}
return Py_FindAttr((PyObject *)self, name);
}
static int
MM_setattro(MM *self, PyObject *name, PyObject *v)
{
if (v && PyString_Check(name))
{
if (strcmp(PyString_AsString(name),"level")==0)
{
self->level=PyInt_AsLong(v);
if (PyErr_Occurred()) return -1;
return 0;
}
}
if (! self->dict && ! (self->dict=PyDict_New())) return -1;
if (v) return PyDict_SetItem(self->dict, name, v);
else return PyDict_DelItem(self->dict, name);
}
static int
MM_length(MM *self)
{
long l=0, el, i;
PyObject *e=0;
UNLESS(-1 != (i=PyList_Size(self->data))) return -1;
while (--i >= 0)
{
e=PyList_GetItem(self->data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}
static PyObject *
MM_subscript(MM *self, PyObject *key)
{
return MM_cget(self, key, 1);
}
typedef struct {
PyObject_HEAD
PyObject *data;
} DictInstance;
static void
DictInstance_dealloc(DictInstance *self)
{
Py_DECREF(self->data);
PyObject_DEL(self);
}
static PyObject *
DictInstance_getattr(DictInstance *self, PyObject *name)
{
PyObject *r;
if ((r=PyObject_GetItem(self->data, name))) return r;
PyErr_SetObject(PyExc_AttributeError, name);
return NULL;
}
static PyTypeObject DictInstanceType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"DictInstance", /*tp_name*/
sizeof(DictInstance), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)DictInstance_dealloc,
(printfunc)0,
(getattrfunc)0,
(setattrfunc)0,
(cmpfunc)0,
(reprfunc)0,
0, 0, 0,
(hashfunc)0,
(ternaryfunc)0,
(reprfunc)0,
(getattrofunc)DictInstance_getattr,
(setattrofunc)0,
0L,0L,
"Wrap a mapping object to look like an instance"
};
static DictInstance *
newDictInstance(PyObject *data)
{
DictInstance *self;
UNLESS(self = PyObject_NEW(DictInstance, &DictInstanceType)) return NULL;
self->data=data;
Py_INCREF(data);
return self;
}
static PyObject *
MM_call(MM *self, PyObject *args, PyObject *kw)
{
PyObject *r, *t;
int i, l=0;
if (args && (l=PyTuple_Size(args)) < 0) return NULL;
if (l)
{
UNLESS(r=PyObject_CallObject(OBJECT(self->ob_type), NULL)) return NULL;
for (i=0; i < l; i++)
if (PyList_Append(((MM*)r)->data, PyTuple_GET_ITEM(args, i)) < 0)
goto err;
if (kw && PyList_Append(((MM*)r)->data, kw) < 0) goto err;
}
else
{
if (!kw)
{
Py_INCREF(Py_None);
return Py_None;
}
r=kw;
Py_INCREF(r);
}
ASSIGN(r, OBJECT(newDictInstance(r)));
UNLESS(t=PyTuple_New(1)) goto err;
PyTuple_SET_ITEM(t, 0, r);
return t;
err:
Py_XDECREF(r);
return NULL;
}
static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char MMtype__doc__[] =
"TemplateDict -- Combine multiple mapping objects for lookup"
;
static PyExtensionClass MMtype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"TemplateDict", /*tp_name*/
sizeof(MM), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)MM_call, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)MM_getattro, /*tp_getattro*/
(setattrofunc)MM_setattro, /*tp_setattro*/
/* Space for future expansion */
0L,0L,
MMtype__doc__, /* Documentation string */
METHOD_CHAIN(MM_methods)
};
/* List of methods defined in the module */
static int
if_finally(PyObject *md, int err)
{
PyObject *t, *v, *tb;
if (err) PyErr_Fetch(&t, &v, &tb);
md=PyObject_GetAttr(md,py__pop);
if (md) ASSIGN(md, PyObject_CallObject(md,NULL));
if (err) PyErr_Restore(t,v,tb);
if (md)
{
Py_DECREF(md);
return -1;
}
else
return -2;
}
static int
render_blocks_(PyObject *blocks, PyObject *rendered,
PyObject *md, PyObject *mda)
{
PyObject *block, *t, *args;
int l, i, k=0, append;
int skip_html_quote;
if ((l=PyList_Size(blocks)) < 0) return -1;
for (i=0; i < l; i++)
{
block=PyList_GET_ITEM(((PyListObject*)blocks), i);
append=1;
if (PyTuple_Check(block)
&& PyTuple_GET_SIZE(block) > 1
&& PyTuple_GET_ITEM(block, 0)
&& PyString_Check(PyTuple_GET_ITEM(block, 0)))
{
switch (PyString_AS_STRING(PyTuple_GET_ITEM(block, 0))[0])
{
case 'v': /* var */
t=PyTuple_GET_ITEM(block,1);
if (t == NULL) return -1;
if (PyString_Check(t)) t=PyObject_GetItem(md, t);
else t=PyObject_CallObject(t, mda);
if (t == NULL) return -1;
skip_html_quote = 0;
if (! ( PyString_Check(t) || PyUnicode_Check(t) ) )
{
/* This might be a TaintedString object */
PyObject *untaintmethod = NULL;
untaintmethod = PyObject_GetAttr(t, untaint_name);
if (untaintmethod) {
/* Quote it */
UNLESS_ASSIGN(t,
PyObject_CallObject(untaintmethod, NULL)) return -1;
skip_html_quote = 1;
} else PyErr_Clear();
Py_XDECREF(untaintmethod);
}
if (! ( PyString_Check(t) || PyUnicode_Check(t) ) )
{
args = PyTuple_New(1);
if(!args) return -1;
PyTuple_SET_ITEM(args,0,t);
t = PyObject_CallObject(ustr, args);
Py_DECREF(args);
args = NULL;
UNLESS(t) return -1;
}
if (skip_html_quote == 0 && PyTuple_GET_SIZE(block) == 3)
/* html_quote */
{
if (PyString_Check(t))
{
if (strchr(PyString_AS_STRING(t), '&') ||
strchr(PyString_AS_STRING(t), '<') ||
strchr(PyString_AS_STRING(t), '>') ||
strchr(PyString_AS_STRING(t), '"') )
{
/* string includes html problem characters, so
we cant skip the quoting process */
skip_html_quote = 0;
}
else
{
skip_html_quote = 1;
}
}
else
{
/* never skip the quoting for unicode strings */
skip_html_quote = 0;
}
if (!skip_html_quote)
{
ASSIGN(t, PyObject_CallFunction(html_quote, "O", t));
if (t == NULL) return -1;
}
}
block = t;
break;
case 'i': /* if */
{
int icond, m, bs;
PyObject *cond, *n, *cache;
bs = PyTuple_GET_SIZE(block) - 1; /* subtract code */
UNLESS(cache=PyDict_New()) return -1;
cond=PyObject_GetAttr(md,py__push);
if (cond) ASSIGN(cond, PyObject_CallFunction(cond,"O",cache));
Py_DECREF(cache);
if (cond) Py_DECREF(cond);
else return -1;
append=0;
m=bs-1;
for (icond=0; icond < m; icond += 2)
{
cond=PyTuple_GET_ITEM(block,icond+1);
if (PyString_Check(cond))
{
/* We have to be careful to handle key errors here */
n=cond;
if ((cond=PyObject_GetItem(md,cond)))
{
if (PyDict_SetItem(cache, n, cond) < 0)
{
Py_DECREF(cond);
return if_finally(md,1);
}
}
else
{
PyObject *t, *v, *tb;
PyErr_Fetch(&t, &v, &tb);
if (t != PyExc_KeyError || PyObject_Compare(v,n))
{
PyErr_Restore(t,v,tb);
return if_finally(md,1);
}
Py_XDECREF(t);
Py_XDECREF(v);
Py_XDECREF(tb);
cond=Py_None;
Py_INCREF(cond);
}
}
else
UNLESS(cond=PyObject_CallObject(cond,mda))
return if_finally(md,1);
if (PyObject_IsTrue(cond))
{
Py_DECREF(cond);
block=PyTuple_GET_ITEM(block,icond+1+1);
if (block!=Py_None &&
render_blocks_(block, rendered, md, mda) < 0)
return if_finally(md,1);
m=-1;
break;
}
else Py_DECREF(cond);
}
if (icond==m)
{
block=PyTuple_GET_ITEM(block,icond+1);
if (block!=Py_None &&
render_blocks_(block, rendered, md, mda) < 0)
return if_finally(md,1);
}
if (if_finally(md,0) == -2) return -1;
}
break;
default:
PyErr_Format(PyExc_ValueError,
"Invalid DTML command code, %s",
PyString_AS_STRING(PyTuple_GET_ITEM(block, 0)));
return -1;
}
}
else if (PyString_Check(block) || PyUnicode_Check(block))
{
Py_INCREF(block);
}
else
{
UNLESS(block=PyObject_CallObject(block,mda)) return -1;
}
if (append && PyObject_IsTrue(block))
{
k=PyList_Append(rendered,block);
Py_DECREF(block);
if (k < 0) return -1;
}
}
return 0;
}
static PyObject *
render_blocks(PyObject *self, PyObject *args)
{
PyObject *md, *blocks, *mda=0, *rendered=0;
int l;
UNLESS(PyArg_ParseTuple(args,"OO", &blocks, &md)) return NULL;
UNLESS(rendered=PyList_New(0)) goto err;
UNLESS(mda=Py_BuildValue("(O)",md)) goto err;
if (render_blocks_(blocks, rendered, md, mda) < 0) goto err;
Py_DECREF(mda);
l=PyList_Size(rendered);
if (l==0)
{
Py_INCREF(py_);
ASSIGN(rendered, py_);
}
else if (l==1)
ASSIGN(rendered, PySequence_GetItem(rendered,0));
else
ASSIGN(rendered, _join_unicode(rendered));
return rendered;
err:
Py_XDECREF(mda);
Py_XDECREF(rendered);
return NULL;
}
static PyObject *
safe_callable(PyObject *self, PyObject *args)
{
PyObject *ob;
int res;
UNLESS(PyArg_ParseTuple(args,"O", &ob)) return NULL;
res = safe_PyCallable_Check(ob);
if (res)
return PyInt_FromLong(1);
else
return PyInt_FromLong(0);
}
static PyObject *
_join_unicode(PyObject *prejoin)
{
PyObject *joined;
joined = PyObject_CallFunction(join,"OO",prejoin,py_);
if(!joined && PyErr_ExceptionMatches(PyExc_UnicodeError))
{
int i,l;
PyObject *list;
PyErr_Clear();
list = PySequence_List(prejoin);
if(!list)
{
return NULL;
}
l = PyList_Size(list);
for(i=0;i<l;++i)
{
PyObject *item = PyList_GetItem(list,i);
if(PyString_Check(item))
{
PyObject *unicode = PyUnicode_DecodeLatin1(PyString_AsString(item),PyString_Size(item),NULL);
if(unicode)
{
PyList_SetItem(list,i,unicode);
}
else
{
Py_DECREF(list);
return NULL;
}
}
}
joined = PyObject_CallFunction(join,"OO",list,py_);
Py_DECREF(list);
}
return joined;
}
static PyObject *
join_unicode(PyObject *self, PyObject *args)
{
PyObject *ob;
UNLESS(PyArg_ParseTuple(args,"O", &ob)) return NULL;
return _join_unicode(ob);
}
static struct PyMethodDef Module_Level__methods[] = {
{"render_blocks", (PyCFunction)render_blocks, METH_VARARGS,
""},
{"join_unicode", (PyCFunction)join_unicode, METH_VARARGS,
"join a list of plain strings into a single plain string,\n"
"a list of unicode strings into a single unicode strings,\n"
"or a list containing a mix into a single unicode string with\n"
"the plain strings converted from latin-1"},
{"safe_callable", (PyCFunction)safe_callable, METH_VARARGS,
"callable() with a workaround for a problem with ExtensionClasses\n"
"and __call__()."},
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
};
void
initcDocumentTemplate(void)
{
PyObject *m, *d;
DictInstanceType.ob_type=&PyType_Type;
UNLESS (html_quote = PyImport_ImportModule("DocumentTemplate.html_quote")) return;
ASSIGN(ustr, PyObject_GetAttrString(html_quote, "ustr"));
UNLESS (ustr) return;
ASSIGN(html_quote, PyObject_GetAttrString(html_quote, "html_quote"));
UNLESS (html_quote) return;
UNLESS(py_isDocTemp=PyString_FromString("isDocTemp")) return;
UNLESS(py_renderNS=PyString_FromString("__render_with_namespace__")) return;
UNLESS(py_blocks=PyString_FromString("blocks")) return;
UNLESS(untaint_name=PyString_FromString("__untaint__")) return;
UNLESS(py_acquire=PyString_FromString("aq_acquire")) return;
UNLESS(py___call__=PyString_FromString("__call__")) return;
UNLESS(py___roles__=PyString_FromString("__roles__")) return;
UNLESS(py__proxy_roles=PyString_FromString("_proxy_roles")) return;
UNLESS(py_guarded_getattr=PyString_FromString("guarded_getattr")) return;
UNLESS(py__push=PyString_FromString("_push")) return;
UNLESS(py__pop=PyString_FromString("_pop")) return;
UNLESS(py_aq_base=PyString_FromString("aq_base")) return;
UNLESS(py_Unauthorized=PyString_FromString("Unauthorized")) return;
UNLESS(py_Unauthorized_fmt=PyString_FromString(
"You are not authorized to access <em>%s</em>.")) return;
UNLESS(py___class__=PyString_FromString("__class__")) return;
UNLESS(py_AUTHENTICATED_USER=PyString_FromString("AUTHENTICATED_USER"))
return;
UNLESS(py_=PyString_FromString("")) return;
UNLESS(join=PyImport_ImportModule("string")) return;
ASSIGN(join,PyObject_GetAttrString(join,"join"));
UNLESS(join) return;
UNLESS(ExtensionClassImported) return;
m = Py_InitModule4("cDocumentTemplate", Module_Level__methods,
cDocumentTemplate_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"InstanceDict",InstanceDictType);
PyExtensionClass_Export(d,"TemplateDict",MMtype);
}
# split off into its own module for aliasing without circrefs
from cgi import escape
from DocumentTemplate.ustr import ustr
def html_quote(v, name='(Unknown name)', md={}):
return escape(ustr(v), 1)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Python implementations of document template some features
XXX This module is no longer actively used, but is left as an
XXX implementation reference for cDocumentTemplate
$Id$"""
__version__='$Revision: 1.42 $'[11:-2]
import sys, types
from types import StringType, UnicodeType, TupleType
from DocumentTemplate.ustr import ustr
import warnings
warnings.warn('pDocumentTemplate is not longer in active use. '
'It remains only as an implementation reference.',
DeprecationWarning)
ClassTypes = [types.ClassType]
try:
from ExtensionClass import Base
except ImportError:
pass
else:
class c(Base): pass
ClassTypes.append(c.__class__)
def safe_callable(ob):
# Works with ExtensionClasses and Acquisition.
if hasattr(ob, '__class__'):
if hasattr(ob, '__call__'):
return 1
else:
return type(ob) in ClassTypes
else:
return callable(ob)
class InstanceDict:
guarded_getattr=None
def __init__(self,o,namespace,guarded_getattr=None):
self.self=o
self.cache={}
self.namespace=namespace
if guarded_getattr is None:
self.guarded_getattr = namespace.guarded_getattr
else: self.guarded_getattr = guarded_getattr
def has_key(self,key):
return hasattr(self.self,key)
def keys(self):
return self.self.__dict__.keys()
def __repr__(self): return 'InstanceDict(%s)' % str(self.self)
def __getitem__(self,key):
cache=self.cache
if cache.has_key(key): return cache[key]
inst=self.self
if key[:1]=='_':
if key != '__str__':
raise KeyError, key # Don't divuldge private data
else:
return str(inst)
get = self.guarded_getattr
if get is None:
get = getattr
try: r = get(inst, key)
except AttributeError: raise KeyError, key
self.cache[key]=r
return r
def __len__(self):
return 1
class MultiMapping:
def __init__(self): self.dicts=[]
def __getitem__(self, key):
for d in self.dicts:
try:
return d[key]
except (KeyError, AttributeError):
# XXX How do we get an AttributeError?
pass
raise KeyError, key
def push(self,d): self.dicts.insert(0,d)
def pop(self, n=1):
r = self.dicts[-1]
del self.dicts[:n]
return r
def keys(self):
kz = []
for d in self.dicts:
kz = kz + d.keys()
return kz
class DictInstance:
def __init__(self, mapping):
self.__d=mapping
def __getattr__(self, name):
try: return self.__d[name]
except KeyError: raise AttributeError, name
class TemplateDict:
level=0
def _pop(self, n=1): return self.dicts.pop(n)
def _push(self, d): return self.dicts.push(d)
def __init__(self):
m=self.dicts=MultiMapping()
self._pop=m.pop
self._push=m.push
try: self.keys=m.keys
except: pass
def __getitem__(self,key,call=1):
v = self.dicts[key]
if call:
if hasattr(v, '__render_with_namespace__'):
return v.__render_with_namespace__(self)
vbase = getattr(v, 'aq_base', v)
if safe_callable(vbase):
if getattr(vbase, 'isDocTemp', 0):
v = v(None, self)
else:
v = v()
return v
def __len__(self):
total = 0
for d in self.dicts.dicts:
total = total + len(d)
return total
def has_key(self,key):
try:
v=self.dicts[key]
except KeyError:
return 0
return 1
getitem=__getitem__
def __call__(self, *args, **kw):
if args:
if len(args)==1 and not kw:
m=args[0]
else:
m=self.__class__()
for a in args: m._push(a)
if kw: m._push(kw)
else: m=kw
return (DictInstance(m),)
def render_blocks(blocks, md):
rendered = []
append=rendered.append
for section in blocks:
if type(section) is TupleType:
l=len(section)
if l==1:
# Simple var
section=section[0]
if type(section) is StringType: section=md[section]
else: section=section(md)
section=ustr(section)
else:
# if
cache={}
md._push(cache)
try:
i=0
m=l-1
while i < m:
cond=section[i]
if type(cond) is StringType:
n=cond
try:
cond=md[cond]
cache[n]=cond
except KeyError, v:
v=str(v)
if n != v: raise KeyError, v, sys.exc_traceback
cond=None
else: cond=cond(md)
if cond:
section=section[i+1]
if section: section=render_blocks(section,md)
else: section=''
m=0
break
i=i+2
if m:
if i==m: section=render_blocks(section[i],md)
else: section=''
finally: md._pop()
elif type(section) is not StringType and type(section) is not UnicodeType:
section=section(md)
if section: rendered.append(section)
l=len(rendered)
if l==0: return ''
elif l==1: return rendered[0]
return join_unicode(rendered)
def join_unicode(rendered):
"""join a list of plain strings into a single plain string,
a list of unicode strings into a single unicode strings,
or a list containing a mix into a single unicode string with
the plain strings converted from latin-1
"""
try:
return ''.join(rendered)
except UnicodeError:
# A mix of unicode string and non-ascii plain strings.
# Fix up the list, treating normal strings as latin-1
rendered = list(rendered)
for i in range(len(rendered)):
if type(rendered[i]) is StringType:
rendered[i] = unicode(rendered[i],'latin-1')
return u''.join(rendered)
change_dtml_documents='Change DTML Documents'
change_dtml_methods='Change DTML Methods'
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Add security system support to Document Templates
"""
# Setup RestrictedDTML
from AccessControl.ImplPython import guarded_getattr
from AccessControl.ZopeGuards import guarded_getitem
RestrictedDTML = None
class BaseRestrictedDTML:
"""A mix-in for derivatives of DT_String.String that adds Zope security."""
def guarded_getattr(self, *args): # ob, name [, default]
return guarded_getattr(*args)
def guarded_getitem(self, ob, index):
return guarded_getitem(ob, index)
# This does not respect the security policy as set by AccessControl. Instead
# it only deals with the C module being compiled or not.
try:
from AccessControl.cAccessControl import RestrictedDTMLMixin
except ImportError:
RestrictedDTML = BaseRestrictedDTML
else:
class RestrictedDTML(RestrictedDTMLMixin, BaseRestrictedDTML):
"""C version of RestrictedDTML."""
# Add security testing capabilities
from AccessControl import SecurityManagement
class DTMLSecurityAPI:
"""API for performing security checks in DTML using '_' methods.
"""
def SecurityValidate(md, inst, parent, name, value):
"""Validate access.
Arguments:
accessed -- the object that was being accessed
container -- the object the value was found in
name -- The name used to access the value
value -- The value retrieved though the access.
The arguments may be provided as keyword arguments. Some of these
arguments may be ommitted, however, the policy may reject access
in some cases when arguments are ommitted. It is best to provide
all the values possible.
"""
return (SecurityManagement
.getSecurityManager()
.validate(inst, parent, name, value)
)
def SecurityCheckPermission(md, permission, object):
"""Check whether the security context allows the given permission on
the given object.
Arguments:
permission -- A permission name
object -- The object being accessed according to the permission
"""
return (SecurityManagement
.getSecurityManager()
.checkPermission(permission, object)
)
def SecurityGetUser(md):
"""Gen the current authenticated user"""
return (SecurityManagement
.getSecurityManager()
.getUser()
)
def SecurityCalledByExecutable(md):
"""Return a boolean value indicating if this context was called
by an executable"""
r = (SecurityManagement
.getSecurityManager()
.calledByExecutable()
)
if r > 0: return r-1
return r
from DocumentTemplate import DT_Util
for name, v in DTMLSecurityAPI.__dict__.items():
if name[0] != '_':
setattr(DT_Util.TemplateDict, name, v)
from types import FunctionType
from AccessControl.ZopeGuards import safe_builtins
for name, v in safe_builtins.items():
if type(v) is FunctionType:
v = DT_Util.NotBindable(v)
if name.startswith('__'):
continue
setattr(DT_Util.TemplateDict, name, v)
# Temporarily create a DictInstance so that we can mark its type as
# being a key in the ContainerAssertions.
from AccessControl.SimpleObjectPolicies import ContainerAssertions
class _dummy_class:
pass
templateDict = DT_Util.TemplateDict()
try:
dictInstance = templateDict(dummy=1)[0]
if type(dictInstance) is not type(_dummy_class()):
ContainerAssertions[type(dictInstance)]=1
except:
# Hmm, this may cause _() and _.namespace() to fail.
# What to do?
pass
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.sequencesort.ssort import *
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__allow_access_to_unprotected_subobjects__ = 1
from zope.sequencesort.ssort import *
res1=[{'weight': 1, 'word': 'AAA', 'key': 'aaa'}, {'weight': 0, 'word': 'BBB', 'key': 'bbb'}, {'weight': 0, 'word': 'CCC', 'key': 'ccc'}, {'weight': 0, 'word': 'DDD', 'key': 'ddd'}, {'weight': 1, 'word': 'EEE', 'key': 'eee'}, {'weight': 0, 'word': 'FFF', 'key': 'fff'}, {'weight': 0, 'word': 'GGG', 'key': 'ggg'}, {'weight': 0, 'word': 'HHH', 'key': 'hhh'}, {'weight': 1, 'word': 'III', 'key': 'iii'}, {'weight': -1, 'word': 'JJJ', 'key': 'jjj'}, {'weight': 0, 'word': 'KKK', 'key': 'kkk'}, {'weight': 0, 'word': 'LLL', 'key': 'lll'}, {'weight': 0, 'word': 'MMM', 'key': 'mmm'}, {'weight': 0, 'word': 'NNN', 'key': 'nnn'}, {'weight': 1, 'word': 'OOO', 'key': 'ooo'}, {'weight': 0, 'word': 'PPP', 'key': 'ppp'}, {'weight': -1, 'word': 'QQQ', 'key': 'qqq'}, {'weight': 0, 'word': 'RRR', 'key': 'rrr'}, {'weight': 0, 'word': 'SSS', 'key': 'sss'}, {'weight': 0, 'word': 'TTT', 'key': 'ttt'}, {'weight': 1, 'word': 'UUU', 'key': 'uuu'}, {'weight': 0, 'word': 'VVV', 'key': 'vvv'}, {'weight': 0, 'word': 'WWW', 'key': 'www'}, {'weight': 0, 'word': 'XXX', 'key': 'xxx'}, {'weight': -1, 'word': 'YYY', 'key': 'yyy'}, {'weight': 0, 'word': 'ZZZ', 'key': 'zzz'}]
res2=[{'weight': 1, 'word': 'AAA', 'key': 'aaa'}, {'weight': 0, 'word': 'BBB', 'key': 'bbb'}, {'weight': 0, 'word': 'CCC', 'key': 'ccc'}, {'weight': 0, 'word': 'DDD', 'key': 'ddd'}, {'weight': 1, 'word': 'EEE', 'key': 'eee'}, {'weight': 0, 'word': 'FFF', 'key': 'fff'}, {'weight': 0, 'word': 'GGG', 'key': 'ggg'}, {'weight': 0, 'word': 'HHH', 'key': 'hhh'}, {'weight': 1, 'word': 'III', 'key': 'iii'}, {'weight': -1, 'word': 'JJJ', 'key': 'jjj'}, {'weight': 0, 'word': 'KKK', 'key': 'kkk'}, {'weight': 0, 'word': 'LLL', 'key': 'lll'}, {'weight': 0, 'word': 'MMM', 'key': 'mmm'}, {'weight': 0, 'word': 'NNN', 'key': 'nnn'}, {'weight': 1, 'word': 'OOO', 'key': 'ooo'}, {'weight': 0, 'word': 'PPP', 'key': 'ppp'}, {'weight': -1, 'word': 'QQQ', 'key': 'qqq'}, {'weight': 0, 'word': 'RRR', 'key': 'rrr'}, {'weight': 0, 'word': 'SSS', 'key': 'sss'}, {'weight': 0, 'word': 'TTT', 'key': 'ttt'}, {'weight': 1, 'word': 'UUU', 'key': 'uuu'}, {'weight': 0, 'word': 'VVV', 'key': 'vvv'}, {'weight': 0, 'word': 'WWW', 'key': 'www'}, {'weight': 0, 'word': 'XXX', 'key': 'xxx'}, {'weight': -1, 'word': 'YYY', 'key': 'yyy'}, {'weight': 0, 'word': 'ZZZ', 'key': 'zzz'}]
res3=[{'weight': 1, 'word': 'AAA', 'key': 'aaa'}, {'weight': 0, 'word': 'BBB', 'key': 'bbb'}, {'weight': 0, 'word': 'CCC', 'key': 'ccc'}, {'weight': 0, 'word': 'DDD', 'key': 'ddd'}, {'weight': 1, 'word': 'EEE', 'key': 'eee'}, {'weight': 0, 'word': 'FFF', 'key': 'fff'}, {'weight': 0, 'word': 'GGG', 'key': 'ggg'}, {'weight': 0, 'word': 'HHH', 'key': 'hhh'}, {'weight': 1, 'word': 'III', 'key': 'iii'}, {'weight': -1, 'word': 'JJJ', 'key': 'jjj'}, {'weight': 0, 'word': 'KKK', 'key': 'kkk'}, {'weight': 0, 'word': 'LLL', 'key': 'lll'}, {'weight': 0, 'word': 'MMM', 'key': 'mmm'}, {'weight': 0, 'word': 'NNN', 'key': 'nnn'}, {'weight': 1, 'word': 'OOO', 'key': 'ooo'}, {'weight': 0, 'word': 'PPP', 'key': 'ppp'}, {'weight': -1, 'word': 'QQQ', 'key': 'qqq'}, {'weight': 0, 'word': 'RRR', 'key': 'rrr'}, {'weight': 0, 'word': 'SSS', 'key': 'sss'}, {'weight': 0, 'word': 'TTT', 'key': 'ttt'}, {'weight': 1, 'word': 'UUU', 'key': 'uuu'}, {'weight': 0, 'word': 'VVV', 'key': 'vvv'}, {'weight': 0, 'word': 'WWW', 'key': 'www'}, {'weight': 0, 'word': 'XXX', 'key': 'xxx'}, {'weight': -1, 'word': 'YYY', 'key': 'yyy'}, {'weight': 0, 'word': 'ZZZ', 'key': 'zzz'}]
res4=[{'weight': 0, 'word': 'ZZZ', 'key': 'zzz'}, {'weight': -1, 'word': 'YYY', 'key': 'yyy'}, {'weight': 0, 'word': 'XXX', 'key': 'xxx'}, {'weight': 0, 'word': 'WWW', 'key': 'www'}, {'weight': 0, 'word': 'VVV', 'key': 'vvv'}, {'weight': 1, 'word': 'UUU', 'key': 'uuu'}, {'weight': 0, 'word': 'TTT', 'key': 'ttt'}, {'weight': 0, 'word': 'SSS', 'key': 'sss'}, {'weight': 0, 'word': 'RRR', 'key': 'rrr'}, {'weight': -1, 'word': 'QQQ', 'key': 'qqq'}, {'weight': 0, 'word': 'PPP', 'key': 'ppp'}, {'weight': 1, 'word': 'OOO', 'key': 'ooo'}, {'weight': 0, 'word': 'NNN', 'key': 'nnn'}, {'weight': 0, 'word': 'MMM', 'key': 'mmm'}, {'weight': 0, 'word': 'LLL', 'key': 'lll'}, {'weight': 0, 'word': 'KKK', 'key': 'kkk'}, {'weight': -1, 'word': 'JJJ', 'key': 'jjj'}, {'weight': 1, 'word': 'III', 'key': 'iii'}, {'weight': 0, 'word': 'HHH', 'key': 'hhh'}, {'weight': 0, 'word': 'GGG', 'key': 'ggg'}, {'weight': 0, 'word': 'FFF', 'key': 'fff'}, {'weight': 1, 'word': 'EEE', 'key': 'eee'}, {'weight': 0, 'word': 'DDD', 'key': 'ddd'}, {'weight': 0, 'word': 'CCC', 'key': 'ccc'}, {'weight': 0, 'word': 'BBB', 'key': 'bbb'}, {'weight': 1, 'word': 'AAA', 'key': 'aaa'}]
res5=[{'weight': -1, 'word': 'JJJ', 'key': 'jjj'}, {'weight': -1, 'word': 'QQQ', 'key': 'qqq'}, {'weight': -1, 'word': 'YYY', 'key': 'yyy'}, {'weight': 0, 'word': 'BBB', 'key': 'bbb'}, {'weight': 0, 'word': 'CCC', 'key': 'ccc'}, {'weight': 0, 'word': 'DDD', 'key': 'ddd'}, {'weight': 0, 'word': 'FFF', 'key': 'fff'}, {'weight': 0, 'word': 'GGG', 'key': 'ggg'}, {'weight': 0, 'word': 'HHH', 'key': 'hhh'}, {'weight': 0, 'word': 'KKK', 'key': 'kkk'}, {'weight': 0, 'word': 'LLL', 'key': 'lll'}, {'weight': 0, 'word': 'MMM', 'key': 'mmm'}, {'weight': 0, 'word': 'NNN', 'key': 'nnn'}, {'weight': 0, 'word': 'PPP', 'key': 'ppp'}, {'weight': 0, 'word': 'RRR', 'key': 'rrr'}, {'weight': 0, 'word': 'SSS', 'key': 'sss'}, {'weight': 0, 'word': 'TTT', 'key': 'ttt'}, {'weight': 0, 'word': 'VVV', 'key': 'vvv'}, {'weight': 0, 'word': 'WWW', 'key': 'www'}, {'weight': 0, 'word': 'XXX', 'key': 'xxx'}, {'weight': 0, 'word': 'ZZZ', 'key': 'zzz'}, {'weight': 1, 'word': 'AAA', 'key': 'aaa'}, {'weight': 1, 'word': 'EEE', 'key': 'eee'}, {'weight': 1, 'word': 'III', 'key': 'iii'}, {'weight': 1, 'word': 'OOO', 'key': 'ooo'}, {'weight': 1, 'word': 'UUU', 'key': 'uuu'}]
res6=[{'weight': -1, 'word': 'YYY', 'key': 'yyy'}, {'weight': -1, 'word': 'QQQ', 'key': 'qqq'}, {'weight': -1, 'word': 'JJJ', 'key': 'jjj'}, {'weight': 0, 'word': 'ZZZ', 'key': 'zzz'}, {'weight': 0, 'word': 'XXX', 'key': 'xxx'}, {'weight': 0, 'word': 'WWW', 'key': 'www'}, {'weight': 0, 'word': 'VVV', 'key': 'vvv'}, {'weight': 0, 'word': 'TTT', 'key': 'ttt'}, {'weight': 0, 'word': 'SSS', 'key': 'sss'}, {'weight': 0, 'word': 'RRR', 'key': 'rrr'}, {'weight': 0, 'word': 'PPP', 'key': 'ppp'}, {'weight': 0, 'word': 'NNN', 'key': 'nnn'}, {'weight': 0, 'word': 'MMM', 'key': 'mmm'}, {'weight': 0, 'word': 'LLL', 'key': 'lll'}, {'weight': 0, 'word': 'KKK', 'key': 'kkk'}, {'weight': 0, 'word': 'HHH', 'key': 'hhh'}, {'weight': 0, 'word': 'GGG', 'key': 'ggg'}, {'weight': 0, 'word': 'FFF', 'key': 'fff'}, {'weight': 0, 'word': 'DDD', 'key': 'ddd'}, {'weight': 0, 'word': 'CCC', 'key': 'ccc'}, {'weight': 0, 'word': 'BBB', 'key': 'bbb'}, {'weight': 1, 'word': 'UUU', 'key': 'uuu'}, {'weight': 1, 'word': 'OOO', 'key': 'ooo'}, {'weight': 1, 'word': 'III', 'key': 'iii'}, {'weight': 1, 'word': 'EEE', 'key': 'eee'}, {'weight': 1, 'word': 'AAA', 'key': 'aaa'}]
res7=[{'weight': -1, 'word': 'JJJ', 'key': 'jjj'}, {'weight': -1, 'word': 'QQQ', 'key': 'qqq'}, {'weight': -1, 'word': 'YYY', 'key': 'yyy'}, {'weight': 0, 'word': 'BBB', 'key': 'bbb'}, {'weight': 0, 'word': 'CCC', 'key': 'ccc'}, {'weight': 0, 'word': 'DDD', 'key': 'ddd'}, {'weight': 0, 'word': 'FFF', 'key': 'fff'}, {'weight': 0, 'word': 'GGG', 'key': 'ggg'}, {'weight': 0, 'word': 'HHH', 'key': 'hhh'}, {'weight': 0, 'word': 'KKK', 'key': 'kkk'}, {'weight': 0, 'word': 'LLL', 'key': 'lll'}, {'weight': 0, 'word': 'MMM', 'key': 'mmm'}, {'weight': 0, 'word': 'NNN', 'key': 'nnn'}, {'weight': 0, 'word': 'PPP', 'key': 'ppp'}, {'weight': 0, 'word': 'RRR', 'key': 'rrr'}, {'weight': 0, 'word': 'SSS', 'key': 'sss'}, {'weight': 0, 'word': 'TTT', 'key': 'ttt'}, {'weight': 0, 'word': 'VVV', 'key': 'vvv'}, {'weight': 0, 'word': 'WWW', 'key': 'www'}, {'weight': 0, 'word': 'XXX', 'key': 'xxx'}, {'weight': 0, 'word': 'ZZZ', 'key': 'zzz'}, {'weight': 1, 'word': 'AAA', 'key': 'aaa'}, {'weight': 1, 'word': 'EEE', 'key': 'eee'}, {'weight': 1, 'word': 'III', 'key': 'iii'}, {'weight': 1, 'word': 'OOO', 'key': 'ooo'}, {'weight': 1, 'word': 'UUU', 'key': 'uuu'}]
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from DocumentTemplate.sequence.SortEx import *
from DocumentTemplate.sequence.tests.ztestlib import *
from DocumentTemplate.sequence.tests.results import *
class TestCase( unittest.TestCase ):
"""
Test SortEx .
"""
def setUp( self ):
"""
"""
def tearDown( self ):
"""
"""
def test1( self ):
"test1"
assert res1==SortEx(wordlist)
def test2( self ):
"test2"
assert res2==SortEx(wordlist, (("key",),), mapping=1)
def test3( self ):
"test3"
assert res3==SortEx(wordlist, (("key", "cmp"),), mapping=1)
def test4( self ):
"test4"
assert res4==SortEx(wordlist, (("key", "cmp", "desc"),), mapping=1)
def test5( self ):
"test5"
assert res5==SortEx(wordlist, (("weight",), ("key",)), mapping=1)
def test6( self ):
"test6"
assert res6==SortEx(wordlist, (("weight",), ("key", "nocase", "desc")), mapping=1)
def test7(self):
"test7"
def myCmp(s1, s2):
return -cmp(s1, s2)
# Create namespace...
from DocumentTemplate.DT_Util import TemplateDict
md = TemplateDict()
#... and push out function onto the namespace
md._push({"myCmp" : myCmp})
assert res7==SortEx( wordlist
, ( ("weight",)
, ("key", "myCmp", "desc")
)
, md
, mapping=1
)
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( TestCase ) )
return suite
def main():
unittest.TextTestRunner().run(test_suite())
if __name__ == '__main__':
main()
class standard_html: # Base class for using with ZTemplates
def __init__(self, title):
self.standard_html_header = """<HTML>
<HEAD>
<TITLE>
%s
</TITLE>
</HEAD>
<BODY>""" % title
self.standard_html_footer = """</BODY>
</HTML>"""
self.title = title
def test(s):
outfile = open("test.out", 'w')
outfile.write(s)
outfile.close()
def exception():
import sys, traceback
exc_type, exc_value, exc_tb = sys.exc_info()
outfile = open("test.err", 'w')
traceback.print_exception(exc_type, exc_value, exc_tb, None, outfile)
outfile.close()
wordlist = [
{"key": "aaa", "word": "AAA", "weight": 1},
{"key": "bbb", "word": "BBB", "weight": 0},
{"key": "ccc", "word": "CCC", "weight": 0},
{"key": "ddd", "word": "DDD", "weight": 0},
{"key": "eee", "word": "EEE", "weight": 1},
{"key": "fff", "word": "FFF", "weight": 0},
{"key": "ggg", "word": "GGG", "weight": 0},
{"key": "hhh", "word": "HHH", "weight": 0},
{"key": "iii", "word": "III", "weight": 1},
{"key": "jjj", "word": "JJJ", "weight": -1},
{"key": "kkk", "word": "KKK", "weight": 0},
{"key": "lll", "word": "LLL", "weight": 0},
{"key": "mmm", "word": "MMM", "weight": 0},
{"key": "nnn", "word": "NNN", "weight": 0},
{"key": "ooo", "word": "OOO", "weight": 1},
{"key": "ppp", "word": "PPP", "weight": 0},
{"key": "qqq", "word": "QQQ", "weight": -1},
{"key": "rrr", "word": "RRR", "weight": 0},
{"key": "sss", "word": "SSS", "weight": 0},
{"key": "ttt", "word": "TTT", "weight": 0},
{"key": "uuu", "word": "UUU", "weight": 1},
{"key": "vvv", "word": "VVV", "weight": 0},
{"key": "www", "word": "WWW", "weight": 0},
{"key": "xxx", "word": "XXX", "weight": 0},
{"key": "yyy", "word": "YYY", "weight": -1},
{"key": "zzz", "word": "ZZZ", "weight": 0}
]
<html><head><title>Inventory by Dealer</title></head><body>
<dl>
<dtml-in inventory mapping size=5 start=first_ad orphan=3>
<dtml-if previous-sequence>
<dtml-in previous-batches mapping>
(<dtml-var batch-start-var-dealer> <dtml-var
batch-start-var-year> <dtml-var
batch-start-var-make> <dtml-var
batch-start-var-model> - <dtml-var
batch-end-var-dealer> <dtml-var
batch-end-var-year> <dtml-var
batch-end-var-make> <dtml-var
batch-end-var-model>)
</dtml-in previous-batches>
</dtml-if previous-sequence>
<dtml-if first-dealer>
<dt><dtml-var dealer></dt><dd>
</dtml-if first-dealer>
<dtml-var year> <dtml-var make> <dtml-var model> <p>
<dtml-if last-dealer>
</dd>
</dtml-if last-dealer>
<dtml-if next-sequence>
<dtml-in next-batches mapping>
(<dtml-var batch-start-var-dealer> <dtml-var
batch-start-var-year> <dtml-var
batch-start-var-make> <dtml-var
batch-start-var-model> - <dtml-var
batch-end-var-dealer> <dtml-var
batch-end-var-year> <dtml-var
batch-end-var-make> <dtml-var
batch-end-var-model>)
</dtml-in next-batches>
</dtml-if next-sequence>
</dtml-in inventory>
</dl>
</body></html>
<html><head><title>Inventory by Dealer</title></head><body>
<dl>
(Bay Chevy 96 Chevrolet Caprice - Bay Chevy 96 Chevrolet Nova)
(Bay Chevy 96 Chevrolet Corvett - Bay Chevy 96 Chevrolet Corsica)
(Bay Chevy 96 Chevrolet Corsica - Colman Olds 96 Olds Ciera)
<dt>Colman Olds</dt><dd>
96 Olds Cutlass <p>
95 Olds Cutlas <p>
93 Dodge Shadow <p>
94 Jeep Cheroke <p>
92 Toyota Previa <p>
</dd>
(Colman Olds 93 Toyota Celica - Colman Olds 94 Honda Civic)
(Colman Olds 93 Honda Civix - Spam Chev 96 Chevrolet Nova)
(Spam Chev 96 Chevrolet Corvett - Spam Chev 96 Chevrolet Corsica)
(Spam Chev 96 Chevrolet Corsica - Spam Olds 96 Olds Ciera)
(Spam Olds 96 Olds Cutlass - Spam Olds 92 Toyota Previa)
(Spam Olds 93 Toyota Celica - Spam Olds 93 Honda Civix)
</dl>
</body></html>
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Document Template Tests
"""
import unittest
class DTMLTests(unittest.TestCase):
def _get_doc_class(self):
from DocumentTemplate.DT_HTML import HTML
return HTML
doc_class = property(_get_doc_class,)
def testBatchingEtc(self):
def item(key, **kw):
return (key, kw)
items=(
item( 1,dealer='Bay Chevy', make='Chevrolet',
model='Caprice', year=96),
item( 2,dealer='Bay Chevy', make='Chevrolet',
model='Nova', year=96),
item( 4,dealer='Bay Chevy', make='Chevrolet',
model='Nova', year=96),
item( 5,dealer='Bay Chevy', make='Chevrolet',
model='Nova', year=96),
item( 3,dealer='Bay Chevy', make='Chevrolet',
model='Corvett', year=96),
item( 6,dealer='Bay Chevy', make='Chevrolet',
model='Lumina', year=96),
item( 7,dealer='Bay Chevy', make='Chevrolet',
model='Lumina', year=96),
item( 8,dealer='Bay Chevy', make='Chevrolet',
model='Lumina', year=95),
item( 9,dealer='Bay Chevy', make='Chevrolet',
model='Corsica', year=96),
item(10,dealer='Bay Chevy', make='Chevrolet',
model='Corsica', year=96),
item(11,dealer='Bay Chevy', make='Toyota',
model='Camry', year=95),
item(12,dealer='Colman Olds', make='Olds',
model='Ciera', year=96),
item(12,dealer='Colman Olds', make='Olds',
model='Ciera', year=96),
item(12,dealer='Colman Olds', make='Olds',
model='Ciera', year=96),
item(12,dealer='Colman Olds', make='Olds',
model='Cutlass', year=96),
item(12,dealer='Colman Olds', make='Olds',
model='Cutlas', year=95),
item(12,dealer='Colman Olds', make='Dodge',
model='Shadow', year=93),
item(12,dealer='Colman Olds', make='Jeep',
model='Cheroke', year=94),
item(12,dealer='Colman Olds', make='Toyota',
model='Previa', year=92),
item(12,dealer='Colman Olds', make='Toyota',
model='Celica', year=93),
item(12,dealer='Colman Olds', make='Toyota',
model='Camry', year=93),
item(12,dealer='Colman Olds', make='Honda',
model='Accord', year=94),
item(12,dealer='Colman Olds', make='Honda',
model='Accord', year=92),
item(12,dealer='Colman Olds', make='Honda',
model='Civic', year=94),
item(12,dealer='Colman Olds', make='Honda',
model='Civix', year=93),
item( 1,dealer='Spam Chev', make='Chevrolet',
model='Caprice', year=96),
item( 2,dealer='Spam Chev', make='Chevrolet',
model='Nova', year=96),
item( 4,dealer='Spam Chev', make='Chevrolet',
model='Nova', year=96),
item( 5,dealer='Spam Chev', make='Chevrolet',
model='Nova', year=96),
item( 3,dealer='Spam Chev', make='Chevrolet',
model='Corvett', year=96),
item( 6,dealer='Spam Chev', make='Chevrolet',
model='Lumina', year=96),
item( 7,dealer='Spam Chev', make='Chevrolet',
model='Lumina', year=96),
item( 8,dealer='Spam Chev', make='Chevrolet',
model='Lumina', year=95),
item( 9,dealer='Spam Chev', make='Chevrolet',
model='Corsica', year=96),
item(10,dealer='Spam Chev', make='Chevrolet',
model='Corsica', year=96),
item(11,dealer='Spam Chevy', make='Toyota',
model='Camry', year=95),
item(12,dealer='Spam Olds', make='Olds',
model='Ciera', year=96),
item(12,dealer='Spam Olds', make='Olds',
model='Ciera', year=96),
item(12,dealer='Spam Olds', make='Olds',
model='Ciera', year=96),
item(12,dealer='Spam Olds', make='Olds',
model='Cutlass', year=96),
item(12,dealer='Spam Olds', make='Olds',
model='Cutlas', year=95),
item(12,dealer='Spam Olds', make='Dodge',
model='Shadow', year=93),
item(12,dealer='Spam Olds', make='Jeep',
model='Cheroke', year=94),
item(12,dealer='Spam Olds', make='Toyota',
model='Previa', year=92),
item(12,dealer='Spam Olds', make='Toyota',
model='Celica', year=93),
item(12,dealer='Spam Olds', make='Toyota',
model='Camry', year=93),
item(12,dealer='Spam Olds', make='Honda',
model='Accord', year=94),
item(12,dealer='Spam Olds', make='Honda',
model='Accord', year=92),
item(12,dealer='Spam Olds', make='Honda',
model='Civic', year=94),
item(12,dealer='Spam Olds', make='Honda',
model='Civix', year=93),
)
html=self.doc_class(read_file('dealers.dtml'))
res = html(inventory=items, first_ad=15)
expected = read_file('dealers.out')
self.assertEqual(res,expected)
def testSequenceSummaries(self):
data=(dict(name='jim', age=38),
# dict(name='kak', age=40),
dict(name='will', age=7),
dict(name='drew', age=4),
dict(name='ches', age=1),
)
html = self.doc_class('<dtml-in data mapping>'
'<dtml-if sequence-end>'
'Variable "name": '
'min=<dtml-var min-name> '
'max=<dtml-var max-name> '
'count=<dtml-var count-name> '
'total=<dtml-var total-name> '
'median=<dtml-var median-name> '
'Variable "age": '
'min=<dtml-var min-age> '
'max=<dtml-var max-age> '
'count=<dtml-var count-age> '
'total=<dtml-var total-age> '
'median=<dtml-var median-age> '
'mean=<dtml-var mean-age> '
'<dtml-let sda=standard-deviation-age>'
's.d.=<dtml-var expr="_.int(sda)">'
'</dtml-let>'
'</dtml-if sequence-end>'
'</dtml-in data>')
res = html(data=data)
expected = ('Variable "name": min=ches max=will count=4 total= '
'median=between jim and drew '
'Variable "age": min=1 max=38 count=4 total=50 '
'median=5 mean=12.5 s.d.=17')
assert res == expected, res
def testDTMLDateFormatting(self):
import DateTime
html = self.doc_class(
"<dtml-var name capitalize spacify> is "
"<dtml-var date fmt=year>/<dtml-var date "
"fmt=month>/<dtml-var date fmt=day>")
res = html(date=DateTime.DateTime("1995-12-25"),
name='christmas_day')
expected = 'Christmas day is 1995/12/25'
assert res == expected, res
def testSimpleString(self):
from DocumentTemplate.DT_HTML import String
dt = String('%(name)s')
res = dt(name='Chris')
expected = 'Chris'
assert res == expected, res
def testStringDateFormatting(self):
import DateTime
from DocumentTemplate.DT_HTML import String
html = String("%(name capitalize spacify)s is "
"%(date fmt=year)s/%(date fmt=month)s/%(date fmt=day)s")
res = html(date=DateTime.DateTime("2001-04-27"),
name='the_date')
expected = 'The date is 2001/4/27'
assert res == expected, res
def testSequence1(self):
html=self.doc_class(
'<dtml-in spam><dtml-in sequence-item><dtml-var sequence-item> '
'</dtml-in sequence-item></dtml-in spam>')
expected = '1 2 3 4 5 6 '
res = html(spam=[[1,2,3],[4,5,6]])
assert res == expected, res
def testSequence2(self):
html=self.doc_class(
'<dtml-in spam><dtml-in sequence-item><dtml-var sequence-item>-'
'</dtml-in sequence-item></dtml-in spam>')
expected = '1-2-3-4-5-6-'
res = html(spam=[[1,2,3],[4,5,6]])
assert res == expected, res
def testNull(self):
html=self.doc_class('<dtml-var spam fmt="$%.2f bobs your uncle" '
'null="spam%eggs!|">')
expected = '$42.00 bobs your unclespam%eggs!|'
res = html(spam=42) + html(spam=None)
assert res == expected, res
def testUrlUnquote(self):
html1 = self.doc_class(
"""
<dtml-var expr="'http%3A//www.zope.org%3Fa%3Db%20123'" fmt=url-unquote>
"""
)
html2 = self.doc_class(
"""
<dtml-var expr="'http%3A%2F%2Fwww.zope.org%3Fa%3Db+123'" fmt=url-unquote-plus>
"""
)
expected = (
"""
http://www.zope.org?a=b 123
"""
)
self.assertEqual(html1(), expected)
self.assertEqual(html2(), expected)
html1 = self.doc_class(
"""
<dtml-var expr="'http%3A//www.zope.org%3Fa%3Db%20123'" url_unquote>
"""
)
html2 = self.doc_class(
"""
<dtml-var expr="'http%3A%2F%2Fwww.zope.org%3Fa%3Db+123'" url_unquote_plus>
"""
)
expected = (
"""
http://www.zope.org?a=b 123
"""
)
self.assertEqual(html1(), expected)
self.assertEqual(html2(), expected)
def test_fmt(self):
html=self.doc_class(
"""
<dtml-var spam>
html=<dtml-var spam fmt=html-quote>
url=<dtml-var spam fmt=url-quote>
multi=<dtml-var spam fmt=multi-line>
dollars=<dtml-var spam fmt=whole-dollars>
cents=<dtml-var spam fmt=dollars-and-cents>
dollars,=<dtml-var spam fmt=dollars-with-commas>
cents,=<dtml-var spam fmt=dollars-and-cents-with-commas>""")
expected = (
'''
4200000
html=4200000
url=4200000
multi=4200000
dollars=$4200000
cents=$4200000.00
dollars,=$4,200,000
cents,=$4,200,000.00
None
html=None
url=None
multi=None
dollars=
cents=
dollars,=
cents,=
<a href="spam">
foo bar
html=&lt;a href=&quot;spam&quot;&gt;
foo bar
url=%3Ca%20href%3D%22spam%22%3E%0Afoo%20bar
multi=<a href="spam"><br />
foo bar
dollars=
cents=
dollars,=
cents,=''')
res = html(spam=4200000) + html(spam=None) + html(
spam='<a href="spam">\nfoo bar')
self.assertEqual(res,expected)
def test_fmt_reST_include_directive_raises(self):
source = '.. include:: /etc/passwd'
html = self.doc_class('<dtml-var name="foo" fmt="restructured-text">')
html._vars['foo'] = source
result = html()
# The include: directive hasn't been rendered, it remains
# verbatimly in the rendered output. Instead a warning
# message is presented:
self.assert_(source in result)
self.assert_(docutils_include_warning in result)
def test_fmt_reST_raw_directive_disabled(self):
from cgi import escape
EXPECTED = '<h1>HELLO WORLD</h1>'
source = '.. raw:: html\n\n %s\n' % EXPECTED
html = self.doc_class('<dtml-var name="foo" fmt="restructured-text">')
html._vars['foo'] = source
result = html()
# The raw: directive hasn't been rendered, it remains
# verbatimly in the rendered output. Instead a warning
# message is presented:
self.assert_(EXPECTED not in result)
self.assert_(escape(EXPECTED) in result)
self.assert_(docutils_raw_warning in result)
def test_fmt_reST_raw_directive_file_option_raises(self):
source = '.. raw:: html\n :file: inclusion.txt'
html = self.doc_class('<dtml-var name="foo" fmt="restructured-text">')
html._vars['foo'] = source
result = html()
# The raw: directive hasn't been rendered, it remains
# verbatimly in the rendered output. Instead a warning
# message is presented:
self.assert_(source in result)
self.assert_(docutils_raw_warning in result)
def test_fmt_reST_raw_directive_url_option_raises(self):
source = '.. raw:: html\n :url: http://www.zope.org'
html = self.doc_class('<dtml-var name="foo" fmt="restructured-text">')
html._vars['foo'] = source
result = html()
# The raw: directive hasn't been rendered, it remains
# verbatimly in the rendered output. Instead a warning
# message is presented:
self.assert_(source in result)
self.assert_(docutils_raw_warning in result)
def testPropogatedError(self):
from ExtensionClass import Base
class foo:
def __len__(self): return 9
def __getitem__(self,i):
if i >= 9: raise IndexError, i
return self.testob(i)
class testob (Base):
__roles__ = None # Public
def __init__(self, index):
self.index = index
self.value = 'item %s' % index
getValue__roles__ = None # Public
def getValue(self):
return self.value
puke__roles__ = None # Public
def puke(self):
raise PukeError('raaalf')
html=self.doc_class(
"""
<dtml-if spam>
<dtml-in spam>
<dtml-var getValue>
<dtml-var puke>
</dtml-in spam>
</dtml-if spam>
""")
try:
html(spam=foo())
except PukeError:
# Passed the test.
pass
else:
assert 0, 'Puke error not propogated'
def testRenderCallable(self):
#Test automatic rendering of callable objects
from ExtensionClass import Base
class C (Base):
__allow_access_to_unprotected_subobjects__ = 1
x=1
def y(self): return self.x*2
C.h = self.doc_class("The h method, <dtml-var x> <dtml-var y>")
C.h2 = self.doc_class("The h2 method")
expected = "1, 2, The h method, 1 2"
res = self.doc_class("<dtml-var x>, <dtml-var y>, <dtml-var h>")(C())
assert res == expected, res
expected = (
'''
1,
2,
The h2 method''')
res = self.doc_class(
'''
<dtml-var expr="_.render(i.x)">,
<dtml-var expr="_.render(i.y)">,
<dtml-var expr="_.render(i.h2)">''')(i=C())
assert res == expected, res
def testWith(self):
class person:
__allow_access_to_unprotected_subobjects__ = 1
name='Jim'
height_inches=73
expected = 'Hi, my name is %s and my height is %d cm.' % (
person.name, int(person.height_inches * 2.54))
res = self.doc_class(
'<dtml-with person>Hi, my name is <dtml-var name> '
'and my height is <dtml-var "_.int(height_inches*2.54)"> '
'cm.</dtml-with>')(person=person)
assert res == expected, res
def testRaise(self):
try:
res = self.doc_class(
"<dtml-raise IndexError>success!</dtml-raise>")()
except IndexError, v:
res = v
assert str(res) == 'success!', `res`
def testNoItemPush(self):
data = dict(sec='B',
name='XXX',
sub=(dict(name='b1'), dict(name='b2',sec='XXX')))
html = """
<dtml-with data mapping><dtml-in sub no_push_item>
<dtml-var sec>.<dtml-with sequence-item mapping><dtml-var name></dtml-with>
</dtml-in></dtml-with>
"""
expected = """
B.b1 B.b2"""
result = self.doc_class(html)(data=data)
assert result == expected, result
def testBasicHTMLIn(self):
data=(
dict(name='jim', age=39),
dict(name='kak', age=29),
dict(name='will', age=8),
dict(name='andrew', age=5),
dict(name='chessie',age=2),
)
html="""
<!--#in data mapping-->
<!--#var name-->, <!--#var age-->
<!--#/in-->
"""
expected = """
jim, 39
kak, 29
will, 8
andrew, 5
chessie, 2
"""
result = self.doc_class(html)(data=data)
assert result == expected, result
def testBasicHTMLIn2(self):
xxx=(Dummy(name=1), Dummy(name=2), Dummy(name=3))
html = """
<!--#in xxx-->
<!--#var name -->
<!--#/in-->
"""
expected = """
1
2
3
"""
result = self.doc_class(html)(xxx=xxx)
assert result == expected, result
def testBasicHTMLIn3(self):
ns = {'prop_ids': ('title', 'id'), 'title': 'good', 'id': 'times'}
html = """:<dtml-in prop_ids><dtml-var sequence-item>=<dtml-var
expr="_[_['sequence-item']]">:</dtml-in>"""
result = self.doc_class(html)(None, ns)
expected = ":title=good:id=times:"
assert result == expected, result
def testHTMLInElse(self):
xxx=(Dummy(name=1), Dummy(name=2), Dummy(name=3))
html="""
<!--#in data mapping-->
<!--#var name-->, <!--#var age-->
<!--#else-->
<!--#in xxx-->
<!--#var name -->
<!--#/in-->
<!--#/in-->
"""
expected = """
1
2
3
"""
result = self.doc_class(html)(xxx=xxx, data={})
assert result == expected, result
def testBasicStringIn(self):
from DocumentTemplate.DT_HTML import String
data=(
dict(name='jim', age=39),
dict(name='kak', age=29),
dict(name='will', age=8),
dict(name='andrew', age=5),
dict(name='chessie',age=2),
)
s="""
%(in data mapping)[
%(name)s, %(age)s
%(in)]
"""
expected = """
jim, 39
kak, 29
will, 8
andrew, 5
chessie, 2
"""
result = String(s)(data=data)
assert expected == result, result
def read_file(name):
import os
from DocumentTemplate import tests
here = tests.__path__[0]
f = open(os.path.join(here, name), 'r')
res = f.read()
f.close()
return res
class Dummy:
__allow_access_to_unprotected_subobjects__ = 1
def __init__(self, **kw):
self.__dict__.update(kw)
def __repr__(self):
return "Dummy(%s)" % `self.__dict__`
docutils_include_warning = '''\
<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">&lt;string&gt;</tt>, line 1)</p>
<p>&quot;include&quot; directive disabled.</p>'''
docutils_raw_warning = '''\
<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">&lt;string&gt;</tt>, line 1)</p>
<p>&quot;raw&quot; directive disabled.</p>'''
class PukeError(Exception):
"""Exception raised in test code."""
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( DTMLTests ) )
return suite
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Document Template Tests
"""
import unittest
class force_str:
# A class whose string representation is not always a plain string:
def __init__(self,s):
self.s = s
def __str__(self):
return self.s
class DTMLUnicodeTests (unittest.TestCase):
def _get_doc_class(self):
from DocumentTemplate.DT_HTML import HTML
return HTML
doc_class = property(_get_doc_class,)
def testAA(self):
html=self.doc_class('<dtml-var a><dtml-var b>')
expected = 'helloworld'
res = html(a='hello',b='world')
assert res == expected, `res`
def testUU(self):
html=self.doc_class('<dtml-var a><dtml-var b>')
expected = u'helloworld'
res = html(a=u'hello',b=u'world')
assert res == expected, `res`
def testAU(self):
html=self.doc_class('<dtml-var a><dtml-var b>')
expected = u'helloworld'
res = html(a='hello',b=u'world')
assert res == expected, `res`
def testAB(self):
html=self.doc_class('<dtml-var a><dtml-var b>')
expected = 'hello\xc8'
res = html(a='hello',b=chr(200))
assert res == expected, `res`
def testUB(self):
html=self.doc_class('<dtml-var a><dtml-var b>')
expected = u'hello\xc8'
res = html(a=u'hello',b=chr(200))
assert res == expected, `res`
def testUB2(self):
html=self.doc_class('<dtml-var a><dtml-var b>')
expected = u'\u07d0\xc8'
res = html(a=unichr(2000),b=chr(200))
assert res == expected, `res`
def testUnicodeStr(self):
html=self.doc_class('<dtml-var a><dtml-var b>')
expected = u'\u07d0\xc8'
res = html(a=force_str(unichr(2000)),b=chr(200))
assert res == expected, `res`
def testUqB(self):
html=self.doc_class('<dtml-var a html_quote><dtml-var b>')
expected = u'he&gt;llo\xc8'
res = html(a=u'he>llo',b=chr(200))
assert res == expected, `res`
def testSize(self):
html=self.doc_class('<dtml-var "_.unichr(200)*4" size=2>')
expected = unichr(200)*2+'...'
res = html()
assert res == expected, `res`
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( DTMLUnicodeTests ) )
return suite
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Document Template Tests
"""
import unittest
from DocumentTemplate import HTML
from DocumentTemplate.tests.testDTML import DTMLTests
from DocumentTemplate.security import RestrictedDTML
from AccessControl import Unauthorized
from AccessControl.SecurityManagement import getSecurityManager
from ExtensionClass import Base
class UnownedDTML(RestrictedDTML, HTML):
def getOwner(self):
return None
def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
"""Render the DTML"""
security = getSecurityManager()
security.addContext(self)
try:
return HTML.__call__(self, client, REQUEST, **kw)
finally:
security.removeContext(self)
class SecurityTests(DTMLTests):
doc_class = UnownedDTML
unrestricted_doc_class = HTML
def testNoImplicitAccess(self):
class person:
name='Jim'
doc = self.doc_class(
'<dtml-with person>Hi, my name is '
'<dtml-var name></dtml-with>')
try:
doc(person=person())
except Unauthorized:
# Passed the test.
pass
else:
assert 0, 'Did not protect class instance'
def testExprExplicitDeny(self):
class myclass (Base):
__roles__ = None # Public
somemethod__roles__ = () # Private
def somemethod(self):
return "This is a protected operation of public object"
html = self.doc_class('<dtml-var expr="myinst.somemethod()">')
self.failUnlessRaises(Unauthorized, html, myinst=myclass())
def testSecurityInSyntax(self):
# Ensures syntax errors are thrown for an expr with restricted
# syntax.
expr = '<dtml-var expr="(lambda x, _read=(lambda ob:ob): x.y)(c)">'
try:
# This would be a security hole.
html = self.doc_class(expr) # It might compile here...
html() # or it might compile here.
except SyntaxError:
# Passed the test.
pass
else:
assert 0, 'Did not catch bad expr'
# Now be sure the syntax error occurred for security purposes.
html = self.unrestricted_doc_class(expr)
class c:
y = 10
res = html(c=c)
assert res == '10', res
def testNewDTMLBuiltins(self):
NEW_BUILTINS_TEMPLATE = """
<dtml-var expr="_.min([1,2])">
<dtml-var expr="_.max([2,3])">
<dtml-var expr="_.sum([1,2,3,4])">
<dtml-var expr="_.hasattr(1, 'foo') and 'Yes' or 'No'">
<dtml-var expr="_.None">
<dtml-var expr="_.string.strip(' testing ')">
<dtml-var expr="[x for x in (1, 2, 3)]">
"""
EXPECTED = ['1', '3', '10', 'No', 'None', 'testing', '[1, 2, 3]']
#
# XXX: these expressions seem like they should work, with
# the following ExPECTED, but they raise Unauthorized
# on the 'next' name.
#
#<dtml-var expr="_.iter([1,2,3]).next()">
#<dtml-var expr="_.enumerate([1,2,3]).next()">
#
#EXPECTED = ['1', '3', '10', '1', '(0, 1)']
template = self.doc_class(NEW_BUILTINS_TEMPLATE)
res = template()
lines = filter(None, [x.strip() for x in res.split('\n')])
self.assertEqual(lines, EXPECTED)
# Note: we need more tests!
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(SecurityTests))
return suite
import unittest
class Test_Raise(unittest.TestCase):
def _getTargetClass(self):
from DocumentTemplate.DT_Raise import Raise
return Raise
def _makeOne(self, type=None, expr=None):
args = []
if type is not None:
args.append('type="%s"' % type)
if expr is not None:
args.append('expr="%s"' % expr)
blocks = [('raise', ' '.join(args), DummySection())]
return self._getTargetClass()(blocks)
def test_ctor_w_type(self):
raiser = self._makeOne(type='Redirect')
self.assertEqual(raiser.__name__, 'Redirect')
self.assertEqual(raiser.expr, None)
def test_ctor_w_expr(self):
raiser = self._makeOne(expr='SyntaxError')
self.assertEqual(raiser.__name__, 'SyntaxError')
self.assertEqual(raiser.expr.expr, 'SyntaxError')
def test_render_w_type_builtin_exception(self):
from DocumentTemplate.DT_Util import TemplateDict
raiser = self._makeOne(type='SyntaxError')
self.assertRaises(SyntaxError, raiser.render, TemplateDict())
def test_render_w_type_zExceptions_exception(self):
from DocumentTemplate.DT_Util import TemplateDict
from zExceptions import Redirect
raiser = self._makeOne(type='Redirect')
self.assertRaises(Redirect, raiser.render, TemplateDict())
def test_render_w_type_nonesuch(self):
from DocumentTemplate.DT_Util import TemplateDict
raiser = self._makeOne(type='NonesuchError')
self.assertRaises(RuntimeError, raiser.render, TemplateDict())
def test_render_w_expr_builtin_exception(self):
from DocumentTemplate.DT_Util import TemplateDict
raiser = self._makeOne(expr='SyntaxError')
self.assertRaises(SyntaxError, raiser.render, TemplateDict())
def test_render_w_expr_zExceptions_exception(self):
from DocumentTemplate.DT_Util import TemplateDict
from zExceptions import Redirect
raiser = self._makeOne(expr='Redirect')
self.assertRaises(Redirect, raiser.render, TemplateDict())
def test_render_w_expr_nonesuch(self):
from DocumentTemplate.DT_Raise import InvalidErrorTypeExpression
from DocumentTemplate.DT_Util import TemplateDict
raiser = self._makeOne(expr='NonesuchError')
self.assertRaises(InvalidErrorTypeExpression,
raiser.render, TemplateDict())
class DummySection:
blocks = ['dummy']
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test_Raise),
))
##############################################################################
#
# Copyright (c) 2008 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests for functions and classes in DT_Var.
$Id$
"""
import unittest, doctest
class TestNewlineToBr(doctest.DocTestCase):
def test_newline_to_br(self):
r"""
newline_to_br should work identically with either DOS-style or
Unix-style newlines.
>>> from DocumentTemplate import DT_Var
>>> text = '''
... line one
... line two
...
... line three
... '''
>>> print DT_Var.newline_to_br(text)
<br />
line one<br />
line two<br />
<br />
line three<br />
<BLANKLINE>
>>> dos = text.replace('\n', '\r\n')
>>> DT_Var.newline_to_br(text) == DT_Var.newline_to_br(dos)
True
"""
def test_newline_to_br_tainted(self):
"""
>>> from DocumentTemplate import DT_Var
>>> text = '''
... <li>line one</li>
... <li>line two</li>
... '''
>>> from AccessControl.tainted import TaintedString
>>> tainted = TaintedString(text)
>>> print DT_Var.newline_to_br(tainted)
<br />
&lt;li&gt;line one&lt;/li&gt;<br />
&lt;li&gt;line two&lt;/li&gt;<br />
<BLANKLINE>
"""
def test_suite():
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite())
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""ustr unit tests.
$Id$
"""
import unittest
class force_str:
# A class whose string representation is not always a plain string:
def __init__(self,s):
self.s = s
def __str__(self):
return self.s
class Foo(str):
pass
class Bar(unicode):
pass
class UnicodeTests(unittest.TestCase):
def test_bare_string_literall(self):
from DocumentTemplate.ustr import ustr
a = ustr('hello')
self.assertEqual(a, 'hello')
def test_with_force_str(self):
from DocumentTemplate.ustr import ustr
a = ustr(force_str('hello'))
self.assertEqual(a, 'hello')
def test_with_non_ascii_char(self):
from DocumentTemplate.ustr import ustr
a = ustr(chr(200))
self.assertEqual(a, chr(200))
def test_with_force_str_non_ascii_char(self):
from DocumentTemplate.ustr import ustr
a = ustr(force_str(chr(200)))
self.assertEqual(a, chr(200))
def test_with_int(self):
from DocumentTemplate.ustr import ustr
a = ustr(22)
self.assertEqual(a, '22')
def test_with_list(self):
from DocumentTemplate.ustr import ustr
a = ustr([1,2,3])
self.assertEqual(a, '[1, 2, 3]')
def test_w_unicode_literal(self):
from DocumentTemplate.ustr import ustr
a = ustr(u'hello')
self.assertEqual(a, 'hello')
def test_w_force_str_unicode_literal(self):
from DocumentTemplate.ustr import ustr
a = ustr(force_str(u'hello'))
self.assertEqual(a, 'hello')
def test_w_unichr(self):
from DocumentTemplate.ustr import ustr
a = ustr(unichr(200))
self.assertEqual(a, unichr(200))
def test_w_force_str_unichr(self):
from DocumentTemplate.ustr import ustr
a = ustr(force_str(unichr(200)))
self.assertEqual(a, unichr(200))
def test_w_unichr_in_exception(self):
from DocumentTemplate.ustr import ustr
a = ustr(ValueError(unichr(200)))
self.assertEqual(a, unichr(200))
def testCustomStrings(self):
from DocumentTemplate.ustr import ustr
a = ustr(Foo('foo'))
self.assertEqual(type(a), Foo)
a = ustr(Bar('bar'))
self.assertEqual(type(a), Bar)
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( UnicodeTests ) )
return suite
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""ustr function.
$Id$
"""
nasty_exception_str = getattr(Exception.__str__, 'im_func', None)
def ustr(v):
"""Convert any object to a plain string or unicode string,
minimising the chance of raising a UnicodeError. This
even works with uncooperative objects like Exceptions
"""
if isinstance(v, basestring):
return v
else:
fn = getattr(v,'__str__',None)
if fn is not None:
# An object that wants to present its own string representation,
# but we dont know what type of string. We cant use any built-in
# function like str() or unicode() to retrieve it because
# they all constrain the type which potentially raises an exception.
# To avoid exceptions we have to call __str__ direct.
if getattr(fn,'im_func',None)==nasty_exception_str:
# Exception objects have been optimised into C, and their
# __str__ function fails when given a unicode object.
# Unfortunately this scenario is all too common when
# migrating to unicode, because of code which does:
# raise ValueError(something_I_wasnt_expecting_to_be_unicode)
return _exception_str(v)
else:
# Trust the object to do this right
v = fn()
if isinstance(v, basestring):
return v
else:
raise ValueError('__str__ returned wrong type')
# Drop through for non-instance types, and instances that
# do not define a special __str__
return str(v)
def _exception_str(exc):
if hasattr(exc, 'args'):
if not exc.args:
return ''
elif len(exc.args) == 1:
return ustr(exc.args[0])
else:
return str(exc.args)
return str(exc)
......@@ -36,8 +36,8 @@ class p_:
davlocked=ImageFile('webdav/www/davlock.gif')
pl=ImageFile('TreeDisplay/www/Plus_icon.gif')
mi=ImageFile('TreeDisplay/www/Minus_icon.gif')
pl=ImageFile('OFS/www/Plus_icon.gif')
mi=ImageFile('OFS/www/Minus_icon.gif')
rtab=ImageFile('App/www/rtab.gif')
ltab=ImageFile('App/www/ltab.gif')
sp =ImageFile('App/www/sp.gif')
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Rendering object hierarchies as Trees
"""
__rcs_id__='$Id$'
__version__='$Revision: 1.58 $'[11:-2]
from binascii import a2b_base64
from binascii import b2a_base64
from cPickle import dumps
import re
from string import translate
from urllib import quote
from urllib import unquote
from zlib import compress
from zlib import decompressobj
#from DocumentTemplate.DT_Util import *
from DocumentTemplate.DT_Util import add_with_prefix
from DocumentTemplate.DT_Util import Eval
from DocumentTemplate.DT_Util import InstanceDict
from DocumentTemplate.DT_Util import name_param
from DocumentTemplate.DT_Util import parse_params
from DocumentTemplate.DT_Util import ParseError
from DocumentTemplate.DT_Util import render_blocks
from DocumentTemplate.DT_Util import simple_name
from DocumentTemplate.DT_String import String
tbl = ''.join(map(chr, range(256)))
tplus = tbl[:ord('+')]+'-'+tbl[ord('+')+1:]
tminus = tbl[:ord('-')]+'+'+tbl[ord('-')+1:]
class Tree:
name='tree'
blockContinuations=()
expand=None
def __init__(self, blocks):
tname, args, section = blocks[0]
args = parse_params(args,
name=None,
expr=None,
nowrap=1,
expand=None,
leaves=None,
header=None,
footer=None,
branches=None,
branches_expr=None,
sort=None,
reverse=1,
skip_unauthorized=1,
id=None,
single=1,
url=None,
# opened_decoration=None,
# closed_decoration=None,
# childless_decoration=None,
assume_children=1,
urlparam=None, prefix=None)
has_key = args.has_key
if has_key('') or has_key('name') or has_key('expr'):
name, expr = name_param(args,'tree',1)
if expr is not None:
args['expr'] = expr
elif has_key(''):
args['name'] = name
else:
name='a tree tag'
if has_key('branches_expr'):
if has_key('branches'):
raise ParseError, _tm(
'branches and and branches_expr given', 'tree')
args['branches_expr'] = Eval(args['branches_expr']).eval
elif not has_key('branches'):
args['branches']='tpValues'
if not has_key('id'):
args['id'] = 'tpId'
if not has_key('url'):
args['url'] = 'tpURL'
if not has_key('childless_decoration'):
args['childless_decoration']=''
prefix = args.get('prefix')
if prefix and not simple_name(prefix):
raise ParseError, _tm(
'prefix is not a simple name', 'tree')
self.__name__ = name
self.section = section.blocks
self.args = args
def render(self, md):
args = self.args
have = args.has_key
if have('name'):
v = md[args['name']]
elif have('expr'):
v = args['expr'].eval(md)
else:
v = md.this
return tpRender(v, md, self.section, self.args)
__call__ = render
String.commands['tree']=Tree
pyid=id # Copy builtin
simple_types = {str: 1, unicode: 1, int: 1, float: 1, long: 1,
tuple: 1, list: 1, dict: 1}
def try_call_attr(ob, attrname, simple_types=simple_types):
attr = getattr(ob, attrname)
if type(attr) in simple_types:
return attr
try:
return attr()
except TypeError:
return attr
def tpRender(self, md, section, args,
try_call_attr=try_call_attr):
"""Render data organized as a tree.
We keep track of open nodes using a cookie. The cookie stored the
tree state. State should be a tree represented like:
[] # all closed
['eagle'], # eagle is open
['eagle'], ['jeep', [1983, 1985]] # eagle, jeep, 1983 jeep and 1985 jeep
where the items are object ids. The state will be pickled to a
compressed and base64ed string that gets unencoded, uncompressed,
and unpickled on the other side.
Note that ids used in state need not be connected to urls, since
state manipulation is internal to rendering logic.
Note that to make unpickling safe, we use the MiniPickle module,
that only creates safe objects
"""
data=[]
idattr=args['id']
if hasattr(self, idattr):
id = try_call_attr(self, idattr)
elif hasattr(self, '_p_oid'): id=oid(self)
else: id=pyid(self)
try:
# see if we are being run as a sub-document
root=md['tree-root-url']
url=md['tree-item-url']
state=md['tree-state']
diff=md['tree-diff']
substate=md['-tree-substate-']
colspan=md['tree-colspan']
level=md['tree-level']
except KeyError:
# OK, we are a top-level invocation
level=-1
if md.has_key('collapse_all'):
state=[id,[]],
elif md.has_key('expand_all'):
have_arg=args.has_key
if have_arg('branches'):
def get_items(node, branches=args['branches'], md=md):
get = md.guarded_getattr
if get is None:
get = getattr
items = get(node, branches)
return items()
elif have_arg('branches_expr'):
def get_items(node, branches_expr=args['branches_expr'], md=md):
md._push(InstanceDict(node, md))
items=branches_expr(md)
md._pop()
return items
state=[id, tpValuesIds(self, get_items, args)],
else:
if md.has_key('tree-s'):
state=md['tree-s']
state=decode_seq(state)
try:
if state[0][0] != id: state=[id,[]],
except IndexError: state=[id,[]],
else: state=[id,[]],
if md.has_key('tree-e'):
diff=decode_seq(md['tree-e'])
apply_diff(state, diff, 1)
if md.has_key('tree-c'):
diff=decode_seq(md['tree-c'])
apply_diff(state, diff, 0)
colspan=tpStateLevel(state)
substate=state
diff=[]
url=''
root=md['URL']
l=root.rfind('/')
if l >= 0: root=root[l+1:]
treeData={'tree-root-url': root,
'tree-colspan': colspan,
'tree-state': state }
prefix = args.get('prefix')
if prefix:
for k, v in treeData.items():
treeData[prefix + k[4:].replace('-', '_')] = v
md._push(InstanceDict(self, md))
md._push(treeData)
try: tpRenderTABLE(self,id,root,url,state,substate,diff,data,colspan,
section,md,treeData, level, args)
finally: md._pop(2)
if state is substate and not (args.has_key('single') and args['single']):
state=state or ([id],)
state=encode_seq(state)
md['RESPONSE'].setCookie('tree-s',state)
return ''.join(data)
def tpRenderTABLE(self, id, root_url, url, state, substate, diff, data,
colspan, section, md, treeData, level=0, args=None,
try_call_attr=try_call_attr,
):
"Render a tree as a table"
have_arg=args.has_key
exp=0
if level >= 0:
urlattr=args['url']
if urlattr and hasattr(self, urlattr):
tpUrl = try_call_attr(self, urlattr)
url = (url and ('%s/%s' % (url, tpUrl))) or tpUrl
root_url = root_url or tpUrl
ptreeData = add_with_prefix(treeData, 'tree', args.get('prefix'))
ptreeData['tree-item-url']=url
ptreeData['tree-level']=level
ptreeData['tree-item-expanded']=0
idattr=args['id']
output=data.append
items=None
if (have_arg('assume_children') and args['assume_children']
and substate is not state):
# We should not compute children unless we have to.
# See if we've been asked to expand our children.
for i in range(len(substate)):
sub=substate[i]
if sub[0]==id:
exp=i+1
break
if not exp: items=1
get=md.guarded_getattr
if get is None:
get = getattr
if items is None:
if have_arg('branches') and hasattr(self, args['branches']):
items = get(self, args['branches'])
items = items()
elif have_arg('branches_expr'):
items=args['branches_expr'](md)
if not items and have_arg('leaves'): items=1
if items and items != 1:
getitem = getattr(md, 'guarded_getitem', None)
if getitem is not None:
unauth=[]
for index in range(len(items)):
try:
getitem(items, index)
except ValidationError:
unauth.append(index)
if unauth:
if have_arg('skip_unauthorized') and args['skip_unauthorized']:
items=list(items)
unauth.reverse()
for index in unauth: del items[index]
else:
raise ValidationError, unauth
if have_arg('sort'):
# Faster/less mem in-place sort
if type(items)==type(()):
items=list(items)
sort=args['sort']
size=range(len(items))
for i in size:
v=items[i]
k=getattr(v,sort)
try: k=k()
except: pass
items[i]=(k,v)
items.sort()
for i in size:
items[i]=items[i][1]
if have_arg('reverse'):
items=list(items) # Copy the list
items.reverse()
diff.append(id)
_td_colspan='<td colspan="%s" style="white-space: nowrap"></td>'
_td_single ='<td width="16" style="white-space: nowrap"></td>'
sub=None
if substate is state:
output('<table cellspacing="0">\n')
sub=substate[0]
exp=items
else:
# Add prefix
output('<tr>\n')
# Add +/- icon
if items:
if level:
if level > 3: output(_td_colspan % (level-1))
elif level > 1: output(_td_single * (level-1))
output(_td_single)
output('\n')
output('<td width="16" valign="top" style="white-space: nowrap">')
for i in range(len(substate)):
sub=substate[i]
if sub[0]==id:
exp=i+1
break
####################################
# Mostly inline encode_seq for speed
s=compress(dumps(diff,1))
if len(s) > 57: s=encode_str(s)
else:
s=b2a_base64(s)[:-1]
l=s.find('=')
if l >= 0: s=s[:l]
s=translate(s, tplus)
####################################
script=md['BASEPATH1']
# Propagate extra args through tree.
if args.has_key( 'urlparam' ):
param = args['urlparam']
param = "%s&" % param
else:
param = ""
if exp:
ptreeData['tree-item-expanded']=1
output('<a name="%s" href="%s?%stree-c=%s#%s">'
'<img src="%s/p_/mi" alt="-" border="0" /></a>' %
(id, root_url, param, s, id, script))
else:
output('<a name="%s" href="%s?%stree-e=%s#%s">'
'<img src="%s/p_/pl" alt="+" border="0" /></a>' %
(id, root_url, param, s, id, script))
output('</td>\n')
else:
if level > 2: output(_td_colspan % level)
elif level > 0: output(_td_single * level)
output(_td_single)
output('\n')
# add item text
dataspan=colspan-level
output('<td%s%s valign="top" align="left">' %
((dataspan > 1 and (' colspan="%s"' % dataspan) or ''),
(have_arg('nowrap') and
args['nowrap'] and ' style="white-space: nowrap"' or ''))
)
output(render_blocks(section, md))
output('</td>\n</tr>\n')
if exp:
level=level+1
dataspan=colspan-level
if level > 2: h=_td_colspan % level
elif level > 0: h=_td_single * level
else: h=''
if have_arg('header'):
doc=args['header']
if md.has_key(doc): doc=md.getitem(doc,0)
else: doc=None
if doc is not None:
output(doc(
None, md,
standard_html_header=(
'<tr>%s'
'<td width="16" style="white-space: nowrap"></td>'
'<td%s valign="top">'
% (h,
(dataspan > 1 and (' colspan="%s"' % dataspan)
or ''))),
standard_html_footer='</td></tr>',
))
if items==1:
# leaves
if have_arg('leaves'):
doc=args['leaves']
if md.has_key(doc): doc=md.getitem(doc,0)
else: doc=None
if doc is not None:
treeData['-tree-substate-']=sub
ptreeData['tree-level']=level
md._push(treeData)
try: output(doc(
None,md,
standard_html_header=(
'<tr>%s'
'<td width="16" style="white-space: nowrap"></td>'
'<td%s valign="top">'
% (h,
(dataspan > 1 and
(' colspan="%s"' % dataspan) or ''))),
standard_html_footer='</td></tr>',
))
finally: md._pop(1)
elif have_arg('expand'):
doc=args['expand']
if md.has_key(doc): doc=md.getitem(doc,0)
else: doc=None
if doc is not None:
treeData['-tree-substate-']=sub
ptreeData['tree-level']=level
md._push(treeData)
try: output(doc(
None,md,
standard_html_header=(
'<tr>%s<td width="16" style="white-space: nowrap"></td>'
'<td%s valign="top">'
% (h,
(dataspan > 1 and
(' colspan="%s"' % dataspan) or ''))),
standard_html_footer='</td></tr>',
))
finally: md._pop(1)
else:
__traceback_info__=sub, args, state, substate
ids={}
for item in items:
if hasattr(item, idattr):
id = try_call_attr(item, idattr)
elif hasattr(item, '_p_oid'): id=oid(item)
else: id=pyid(item)
if len(sub)==1: sub.append([])
substate=sub[1]
ids[id]=1
md._push(InstanceDict(item,md))
try: data=tpRenderTABLE(
item,id,root_url,url,state,substate,diff,data,
colspan, section, md, treeData, level, args)
finally: md._pop()
if not sub[1]: del sub[1]
ids=ids.has_key
for i in range(len(substate)-1,-1):
if not ids(substate[i][0]): del substate[i]
if have_arg('footer'):
doc=args['footer']
if md.has_key(doc): doc=md.getitem(doc,0)
else: doc=None
if doc is not None:
output(doc(
None, md,
standard_html_header=(
'<tr>%s<td width="16" style="white-space: nowrap"></td>'
'<td%s valign="top">'
% (h,
(dataspan > 1 and (' colspan="%s"' % dataspan)
or ''))),
standard_html_footer='</td></tr>',
))
del diff[-1]
if not diff: output('</table>\n')
return data
def apply_diff(state, diff, expand):
if not diff: return
s=[None, state]
diff.reverse()
__traceback_info__=s, diff
while diff:
id=diff[-1]
del diff[-1]
if len(s)==1: s.append([])
s=s[1]
if type(s)==type(()):
s=list(s)
loc=-1
for i in range(len(s)):
if s[i][0]==id:
loc=i
break
if loc >= 0:
if not diff and not expand:
del s[loc]
else:
s=s[loc]
elif diff or expand:
s.append([id,[]])
s=s[-1][1]
while diff:
id=diff[-1]
del diff[-1]
if diff or expand:
s.append([id,[]])
s=s[-1][1]
def encode_seq(state):
"Convert a sequence to an encoded string"
state=compress(dumps(state))
l=len(state)
if l > 57:
states=[]
for i in range(0,l,57):
states.append(b2a_base64(state[i:i+57])[:-1])
state=''.join(states)
else: state=b2a_base64(state)[:-1]
l=state.find('=')
if l >= 0: state=state[:l]
state=translate(state, tplus)
return state
def encode_str(state):
"Convert a sequence to an encoded string"
l=len(state)
if l > 57:
states=[]
for i in range(0,l,57):
states.append(b2a_base64(state[i:i+57])[:-1])
state=''.join(states)
else: state=b2a_base64(state)[:-1]
l=state.find('=')
if l >= 0: state=state[:l]
state=translate(state, tplus)
return state
def decode_seq(state):
"Convert an encoded string to a sequence"
state=translate(state, tminus)
l=len(state)
if l > 76:
states=[]
j=0
for i in range(l/76):
k=j+76
states.append(a2b_base64(state[j:k]))
j=k
if j < l:
state=state[j:]
l=len(state)
k=l%4
if k: state=state+'='*(4-k)
states.append(a2b_base64(state))
state=''.join(states)
else:
l=len(state)
k=l%4
if k: state=state+'='*(4-k)
state=a2b_base64(state)
state=decompress(state)
try: return list(MiniUnpickler(StringIO(state)).load())
except: return []
def decompress(input,max_size=10240):
# This sillyness can go away in python 2.2
d = decompressobj()
output = ''
while input:
fragment_size = max(1,(max_size-len(output))/1000)
fragment,input = input[:fragment_size],input[fragment_size:]
output += d.decompress(fragment)
if len(output)>max_size:
raise ValueError('Compressed input too large')
return output+d.flush()
def tpStateLevel(state, level=0):
for sub in state:
if len(sub)==2: level = max(level, 1+tpStateLevel(sub[1]))
else: level=max(level,1)
return level
def tpValuesIds(self, get_items, args,
try_call_attr=try_call_attr,
):
# get_item(node) is a function that returns the subitems of node
# This should build the ids of subitems which are
# expandable (non-empty). Leaves should never be
# in the state - it will screw the colspan counting.
r=[]
idattr=args['id']
try:
try: items=get_items(self)
except AttributeError: items=()
for item in items:
try:
if get_items(item):
if hasattr(item, idattr):
id = try_call_attr(item, idattr)
elif hasattr(item, '_p_oid'): id=oid(item)
else: id=pyid(item)
e=tpValuesIds(item, get_items, args)
if e: id=[id,e]
else: id=[id]
r.append(id)
except: pass
except: pass
return r
def oid(self):
return b2a_base64(str(self._p_oid))[:-1]
#icoSpace='<IMG SRC="Blank_icon" BORDER="0">'
#icoPlus ='<IMG SRC="Plus_icon" BORDER="0">'
#icoMinus='<IMG SRC="Minus_icon" BORDER="0">'
###############################################################################
## Everthing below here should go in a MiniPickle.py module, but keeping it
## internal makes an easier patch
import pickle
from cStringIO import StringIO
if pickle.format_version!="2.0":
# Maybe the format changed, and opened a security hole
raise 'Invalid pickle version'
class MiniUnpickler(pickle.Unpickler):
"""An unpickler that can only handle simple types.
"""
def refuse_to_unpickle(self):
raise pickle.UnpicklingError, 'Refused'
dispatch = pickle.Unpickler.dispatch.copy()
for k,v in dispatch.items():
if k=='' or k in '().012FGIJKLMNTUVXS]adeghjlpqrstu}':
# This key is necessary and safe, so leave it in the map
pass
else:
dispatch[k] = refuse_to_unpickle
# Anything unnecessary is banned, but here is some logic to explain why
if k in [pickle.GLOBAL, pickle.OBJ, pickle.INST, pickle.REDUCE, pickle.BUILD]:
# These are definite security holes
pass
elif k in [pickle.PERSID, pickle.BINPERSID]:
# These are just unnecessary
pass
del k
del v
def _should_succeed(x,binary=1):
if x != MiniUnpickler(StringIO(pickle.dumps(x,binary))).load():
raise ValueError(x)
def _should_fail(x,binary=1):
try:
MiniUnpickler(StringIO(pickle.dumps(x,binary))).load()
raise ValueError(x)
except pickle.UnpicklingError, e:
if e[0]!='Refused': raise ValueError(x)
class _junk_class:
pass
def _test():
_should_succeed('hello')
_should_succeed(1)
_should_succeed(1L)
_should_succeed(1.0)
_should_succeed((1,2,3))
_should_succeed([1,2,3])
_should_succeed({1:2,3:4})
_should_fail(open)
_should_fail(_junk_class)
_should_fail(_junk_class())
# Test MiniPickle on every import
_test()
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import TreeTag
......@@ -7,6 +7,7 @@ Zope2 =
AccessControl = 2.13.1
Acquisition = 2.13.3
DateTime = 2.12.2
DocumentTemplate = 2.13.0
ExtensionClass = 2.13.2
initgroups = 2.13.0
Missing = 2.13.1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment