[Zope3-checkins] CVS: Zope3/src/zope/documenttemplate - __init__.py:1.1.2.1 documenttemplate.py:1.1.2.1 dt_html.py:1.1.2.1 dt_if.py:1.1.2.1 dt_in.py:1.1.2.1 dt_insv.py:1.1.2.1 dt_let.py:1.1.2.1 dt_raise.py:1.1.2.1 dt_return.py:1.1.2.1 dt_string.py:1.1.2.1 dt_try.py:1.1.2.1 dt_util.py:1.1.2.1 dt_var.py:1.1.2.1 dt_with.py:1.1.2.1 pdocumenttemplate.py:1.1.2.1

Jim Fulton jim@zope.com
Mon, 23 Dec 2002 14:32:48 -0500


Update of /cvs-repository/Zope3/src/zope/documenttemplate
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/documenttemplate

Added Files:
      Tag: NameGeddon-branch
	__init__.py documenttemplate.py dt_html.py dt_if.py dt_in.py 
	dt_insv.py dt_let.py dt_raise.py dt_return.py dt_string.py 
	dt_try.py dt_util.py dt_var.py dt_with.py pdocumenttemplate.py 
Log Message:
Initial renaming before debugging

=== Added File Zope3/src/zope/documenttemplate/__init__.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.

$Id: __init__.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
"""

from zope.documenttemplate.documenttemplate import String, HTML
from zope.documenttemplate.documenttemplate import html_quote


=== Added File Zope3/src/zope/documenttemplate/documenttemplate.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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:

         <dtml-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.

Document Templates may be created 2 ways by default:

    DocumentTemplate.String -- Creates a document templated from a
        string using an extended form of python string formatting.

    DocumentTemplate.HTML -- Creates a document templated from a
        string using HTML server-side-include rather than
        python-format-string syntax.

$Id: documenttemplate.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
'''

ParseError='Document Template Parse Error'

from zope.documenttemplate.dt_string import String
from zope.documenttemplate.dt_html import HTML
from zope.documenttemplate.dt_util import html_quote


=== Added File Zope3/src/zope/documenttemplate/dt_html.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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: dt_html.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
"""
import re
from zope.documenttemplate.dt_string import String
from zope.documenttemplate.dt_util import ParseError, str

class DTMLRegExClass:

    def search(self, text, start=0,
               name_match=re.compile('[\0- ]*[a-zA-Z]+[\0- ]*').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+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 and 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:

      <dtml-in results>
        <dtml-var name>
      </dtml-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.
    """

    def tagre(self):
        return DTMLRegExClass()


    def parseTag(self, tagre, 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, =tagre.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)


    def SubTemplate(self, name):
        return HTML('', __name__=name)

    def varExtra(self,tagre):
        return 's'

    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()



=== Added File Zope3/src/zope/documenttemplate/dt_if.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
# 
##############################################################################
"""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::

          <dtml-if name>
               text 
          </dtml-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::

          <dtml-else name>
               text 
          </dtml-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::

          <dtml-if name>
               true text 
          <dtml-else name>
               false text 
          </dtml-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.

$Id: dt_if.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
""" 
from zope.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 = 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 = (cond, None, section.blocks)


class Else(Unless):
    # The else tag is included for backward compatibility and is deprecated.
    name = 'else'


=== Added File Zope3/src/zope/documenttemplate/dt_in.py === (685/785 lines abridged)
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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::

          <dtml-in name>
               text 
          </dtml-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::

         <dtml-in sequence>some markup</dtml-in>

      A more complete case is used as follows::

        <dtml-in sequence sort=age>
          <dtml-var sequence-number>) <dtml-var age>
        </dtml-in>


[-=- -=- -=- 685 lines omitted -=- -=- -=-]

                                 akey = akey()
                             except:
                                 pass
                         k.append(akey)
                 else: # One sort key.
                     try:
                         if mapping:
                             k = v[sort]
                         else:
                             k = getattr(v, sort)
                     except AttributeError, KeyError:
                         k = None
                     if not basic_type(type(k)):           
                         try:
                             k = k()
                         except:
                             pass

            s.append((k,client))

        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 = {StringType: 1, IntType: 1, FloatType: 1, TupleType: 1,
              ListType: 1, NoneType: 1}.has_key

def int_param(params, md, name, default=0, st=StringType):
    try:
        v = params[name]
    except:
        v = default
    if v:
        try:
            v = v.atoi()
        except:
            v = md[v]
            if isinstance(v, st):
                v = v.atoi()
    return v


=== Added File Zope3/src/zope/documenttemplate/dt_insv.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 variables support

$Id: dt_insv.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
"""

from math import sqrt
from types import IntType, TupleType

try:
    import Missing
    mv = Missing.Value
except: mv = None


class sequence_variables:

    def __init__(self, items=None, query_string='', start_name_re=None):
        self.items = items
        self.query_string = query_string
        self.start_name_re = start_name_re

        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 = TupleType):
        i = self.items[index]
        if isinstance(i, 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 = sub('DCCCC', 'CM', roman)
        roman = sub('CCCC', 'CD', roman)
        roman = sub('LXXXX', 'XC', roman)
        roman = sub('XXXX', 'XL', roman)
        roman = sub('VIIII', 'IX', roman)
        roman = sub('IIII', 'IV', roman)

        return roman


    def value(self, index, name):
        data = self.data
        item = self.items[index]
        if isinstance(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.data['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                  
            re = self.start_name_re
            l = re.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.data['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:
                    item = getattr(item, name)
                try:
                    if item is mv:
                        item = None
                    if isinstance(item, IntType):
                        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 __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:
            raise KeyError, key

        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 sub(s1, s2, src):
    return s2.join(src.split(s1))

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 end > 0:
            if end < start:
                end = start
        else:
            end = start + size-1
            try:
                sequence[end+orphan-1]
            except:
                end = len(sequence)

    elif end > 0:
        try:
            sequence[end-1]
        except:
            end=len(sequence)
        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)

    return start, end, size


=== Added File Zope3/src/zope/documenttemplate/dt_let.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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::

     <dtml-let foofunc="foo()" my_bar=bar>
       foo() = <dtml-var foofunc>,
       bar = <dtml-var my_bar>
     </dtml-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.

$Id: dt_let.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
"""
from zope.documenttemplate.dt_util import render_blocks, Eval, ParseError
from zope.documenttemplate.dt_util import str # Probably needed due to hysterical pickles.

from types import StringType
import re

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 isinstance(expr, StringType):
                    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 apply(parse_let_params, (text, result,tag), parms)
    else:
        return result


=== Added File Zope3/src/zope/documenttemplate/dt_raise.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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::

    <dtml-if expr="condition_that_tests_input">
       <dtml-raise type="Input Error">
           The value you entered is not valid
       </dtml-raise>
    </dtml-if>

$Id: dt_raise.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
''' 
from zope.documenttemplate.dt_util import parse_params, name_param, render_blocks, str

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 = self.__name__
                if t[-5:] == 'Error' and __builtins__.has_key(t):
                    t = __builtins__[t]
        else:
            try:
                t = expr.eval(md)
            except:
                t = 'Invalid Error Type Expression'

        try:
            v = render_blocks(self.section,md)
        except:
            v = 'Invalid Error Value'

        raise t, v

    __call__ = render


=== Added File Zope3/src/zope/documenttemplate/dt_return.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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: dt_return.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
""" 
from zope.documenttemplate.dt_util import parse_params, name_param, html_quote, str


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__, self.expr = name, expr

    def render(self, md):
        name = self.__name__
        val = self.expr
        if val is None:
            val = md[name]
        else:
            val = val.eval(md)

        raise DTReturn(val)

    __call__ = render


class DTReturn:
    def __init__(self, v):
        self.v = v


=== Added File Zope3/src/zope/documenttemplate/dt_string.py === (405/505 lines abridged)
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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: dt_string.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
""" 
import re, thread

from zope.documenttemplate.dt_util import ParseError, InstanceDict, TemplateDict, render_blocks, str
from zope.documenttemplate.dt_var import Var, Call, Comment
from zope.documenttemplate.dt_return import ReturnTag, DTReturn

from types import TupleType

_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)]

    """ 

    # Document Templates masquerade as functions:
    class func_code:
        pass
    func_code = func_code()
    func_code.co_varnames = 'self', 'REQUEST'

[-=- -=- -=- 405 lines omitted -=- -=- -=-]

        if level > 200:
            raise SystemError, ('infinite recursion in document template')
        md.level = level+1

        if client is not None:
            if isinstance(client, TupleType):
                # 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 += 1
            else:
                # otherwise its just a normal client object.
                push(InstanceDict(client, md)) # Circ. Ref. 8-|
                pushed += 1
                
        if self._vars: 
            push(self._vars)
            pushed += 1

        if kw:
            push(kw)
            pushed += 1

        try:
            try:
                result = render_blocks(self._v_blocks, md)
            except DTReturn, v:
                result = v.v
            return result
        finally:
            if pushed:
                md._pop(pushed) # Get rid of circular reference!
            md.level=level # Restore previous level


    validate=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
    


=== Added File Zope3/src/zope/documenttemplate/dt_try.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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: dt_try.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
""" 

import sys, traceback
from StringIO import StringIO
from zope.documenttemplate.dt_util import ParseError, parse_params, render_blocks
from zope.documenttemplate.dt_util import namespace, InstanceDict
from zope.documenttemplate.dt_return import DTReturn

from types import StringType


class Try:
    """Zope DTML Exception handling
    
    usage:
    
    <dtml-try>
    <dtml-except SomeError AnotherError>
    <dtml-except YetAnotherError>
    <dtml-except>
    <dtml-else>
    </dtml-try>
      
    or:
      
    <dtml-try>
    <dtml-finally>
    </dtml-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 isinstance(t, StringType):
                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 isinstance(exception, StringType):
            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


=== Added File Zope3/src/zope/documenttemplate/dt_util.py === (408/508 lines abridged)
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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: dt_util.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
""" 
import re, math
import whrandom

from __builtin__ import str  # XXX needed for pickling (legacy)
from types import ListType, StringType, TupleType

from zope.documenttemplate.pdocumenttemplate import InstanceDict, TemplateDict, render_blocks


ParseError='Document Template Parse Error'
ValidationError='Unauthorized'


def html_quote(v, name='(Unknown name)', md={},
               character_entities=(
                       (('&'),    '&amp;'),
                       (('<'),    '&lt;' ),
                       (('>'),    '&gt;' ),
                       (('"'),    '&quot;'))): #"
        text = str(v)
        for re, name in character_entities:
            text = text.replace(re, name)
        return text


def int_param(params, md, name, default=0):
    try:
        v = params[name]
    except:
        v = default
    if v:
        try:
            v = v.atoi()

[-=- -=- -=- 408 lines omitted -=- -=- -=-]

        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 apply(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 apply(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 apply(parse_params, (text,result), parms)
    else:
        return result


=== Added File Zope3/src/zope/documenttemplate/dt_var.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 HTML source::

          <!--#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.

       '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       
       '<!--#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.

   
$Id: dt_var.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
"""
from zope.documenttemplate.dt_util import parse_params, name_param, html_quote, str
import re, sys
from urllib import quote, quote_plus


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, 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 = expr,


    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'):
            fmt=args['fmt']
            if have_arg('null') and not val and val != 0:
                try:
                    if hasattr(val, fmt):
                        val = getattr(val,fmt)()
                    elif special_formats.has_key(fmt):
                        val = special_formats[fmt](val, name, md)
                    elif fmt == '':
                        val = ''
                    else:
                        val = fmt % val
                except:
                    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 = getattr(val,fmt)()
                elif special_formats.has_key(fmt):
                    val = special_formats[fmt](val, name, md)
                elif fmt == '':
                    val = ''
                else:
                    val = fmt % val

        # finally, pump it through the actual string format...
        fmt=self.fmt
        if fmt == 's':
            val = str(val)
        else:
            val = ('%'+self.fmt) % (val,)

        # next, look for upper, lower, etc
        for f in self.modifiers:
            val = f(val)

        if have_arg('size'):
            size = args['size']
            try:
                size = int(size)
            except:
                raise 'Document Error',(
                '''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 += l

        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 = 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 newline_to_br(v, name='(Unknown name)', md={}):
    v = str(v)
    if v.find('\r') >= 0:
        v = ''.join(v.split('\r'))
    if v.find('\n') >= 0:
        v = '<br>\n'.join(v.split('\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)))


StructuredText=None
def structured_text(v, name='(Unknown name)', md={}):
    global StructuredText
    if StructuredText is None:
        import StructuredText
    return str(StructuredText.html_with_references(str(v), 3))


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 "''".join(v.split("'"))
    return v


special_formats={
    'whole-dollars': whole_dollars,
    'dollars-and-cents': dollars_and_cents,
    'collection-length': len_format,
    # XXX: Gone for now
    # 'structured-text': structured_text,

    # The rest are depricated:
    'sql-quote': sql_quote,
    'html-quote': html_quote,
    'url-quote': url_quote,
    'url-quote-plus': url_quote_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 = ' '.join(val.split('_'))
    return val


def lower(val):
    return val.lower()


def upper(val):
    return val.upper()


def capitalize(val):
    return val.capitalize()


modifiers = (html_quote, url_quote, url_quote_plus, newline_to_br,
             lower, upper, capitalize, spacify,
             thousands_commas, sql_quote)
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::
    
      <dtml-comment>
      
        This text is not rendered.

      </dtml-comment>
    ''' 
    name = 'comment'
    blockContinuations = ()

    def __init__(self, args, fmt=''):
        pass

    def render(self, md):
        return ''

    __call__=render


=== Added File Zope3/src/zope/documenttemplate/dt_with.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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::

     <dtml-with person>
       <dtml-var name>,
       <dtml-var age>
     </dtml-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.

$Id: dt_with.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
"""

from zope.documenttemplate.dt_util import parse_params, name_param, InstanceDict, render_blocks, str
from zope.documenttemplate.dt_util import TemplateDict

from types import StringType, TupleType


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 isinstance(expr, StringType):
            v = md[expr]
        else:
            v = expr(md)

        if not self.mapping:
            if isinstance(v, TupleType) and len(v) == 1:
                v = v[0]
            v = InstanceDict(v, md)

        if self.only:
            _md = md
            md = TemplateDict()
            if hasattr(_md, 'validate'):
                md.validate = _md.validate

        md._push(v)
        try:
            return render_blocks(self.section, md)
        finally:
            md._pop(1)

    __call__ = render


=== Added File Zope3/src/zope/documenttemplate/pdocumenttemplate.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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

$Id: pdocumenttemplate.py,v 1.1.2.1 2002/12/23 19:32:46 jim Exp $
"""

import sys
from types import StringTypes, TupleType, ClassType
ClassTypes = [ClassType]


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:

    def __init__(self, o, namespace):
        self.self = o
        self.cache = {}
        self.namespace = namespace

    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)

        try:
            r = getattr(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):
                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):
                v = v()
        return v

    def __len__(self):
       total = 0
       for d in self.dicts.dicts:
           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 isinstance(section, StringTypes):
                    section = md[section]
                else:
                    section = section(md)
                section = str(section)
            else:
                # if
                cache = {}
                md._push(cache)
                try:
                    i = 0
                    m = l-1
                    while i < m:
                        cond = section[i]
                        if isinstance(cond, StringTypes):
                            n = cond
                            try:
                                cond = md[cond]
                                cache[n] = cond
                            except KeyError, v:
                                v = v[0]
                                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 += 2
                    if m:
                        if i == m:
                            section = render_blocks(section[i],md)
                        else:
                            section = ''

                finally: md._pop()

        elif not isinstance(section, StringTypes):
            section = section(md)

        if section:
            rendered.append(section)

    l = len(rendered)
    if l == 0:
        return ''
    elif l == 1:
        return rendered[0]
    return ''.join(rendered)
    return rendered