Code

bugfixes for redirect and form handling
[roundup.git] / roundup / cgi / PageTemplates / PageTemplate.py
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE
11
12 ##############################################################################
13 """Page Template module
15 HTML- and XML-based template objects using TAL, TALES, and METAL.
18 Modified for Roundup 0.5 release:
20 - changed imports to import from roundup.cgi
22 """
24 __version__='$Revision: 1.2 $'[11:-2]
26 import sys
28 from roundup.cgi.TAL.TALParser import TALParser
29 from roundup.cgi.TAL.HTMLTALParser import HTMLTALParser
30 from roundup.cgi.TAL.TALGenerator import TALGenerator
31 from roundup.cgi.TAL.TALInterpreter import TALInterpreter
32 from Expressions import getEngine
33 from string import join, strip, rstrip, split, replace, lower, find
34 from cStringIO import StringIO
35 from ComputedAttribute import ComputedAttribute
37 class PageTemplate:
38     "Page Templates using TAL, TALES, and METAL"
39      
40     content_type = 'text/html'
41     expand = 0
42     _v_errors = ()
43     _v_warnings = ()
44     _v_program = None
45     _v_macros = None
46     _v_cooked = 0
47     id = '(unknown)'
48     _text = ''
49     _error_start = '<!-- Page Template Diagnostics'
51     def macros(self):
52         return self.pt_macros()
53     macros = ComputedAttribute(macros, 1)
55     def pt_edit(self, text, content_type):
56         if content_type:
57             self.content_type = str(content_type)
58         if hasattr(text, 'read'):
59             text = text.read()
60         self.write(text)
62     def pt_getContext(self):
63         c = {'template': self,
64              'options': {},
65              'nothing': None,
66              'request': None,
67              'modules': ModuleImporter,
68              }
69         parent = getattr(self, 'aq_parent', None)
70         if parent is not None:
71             c['here'] = parent
72             c['container'] = self.aq_inner.aq_parent
73             while parent is not None:
74                 self = parent
75                 parent = getattr(self, 'aq_parent', None)
76             c['root'] = self
77         return c
78     
79     def pt_render(self, source=0, extra_context={}):
80         """Render this Page Template"""
81         if not self._v_cooked:
82             self._cook()
84         __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
86         if self._v_errors:
87             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
88         output = StringIO()
89         c = self.pt_getContext()
90         c.update(extra_context)
92         TALInterpreter(self._v_program, self._v_macros,
93                        getEngine().getContext(c),
94                        output,
95                        tal=not source, strictinsert=0)()
96         return output.getvalue()
98     def __call__(self, *args, **kwargs):
99         if not kwargs.has_key('args'):
100             kwargs['args'] = args
101         return self.pt_render(extra_context={'options': kwargs})
103     def pt_errors(self):
104         if not self._v_cooked:
105             self._cook()
106         err = self._v_errors
107         if err:
108             return err
109         if not self.expand: return
110         try:
111             self.pt_render(source=1)
112         except:
113             return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
114         
115     def pt_warnings(self):
116         if not self._v_cooked:
117             self._cook()
118         return self._v_warnings
120     def pt_macros(self):
121         if not self._v_cooked:
122             self._cook()
123         if self._v_errors:
124             __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
125             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
126         return self._v_macros
128     def pt_source_file(self):
129         return None  # Unknown.
131     def write(self, text):
132         assert type(text) is type('')
133         if text[:len(self._error_start)] == self._error_start:
134             errend = find(text, '-->')
135             if errend >= 0:
136                 text = text[errend + 4:]
137         if self._text != text:
138             self._text = text
139         self._cook()
141     def read(self):
142         if not self._v_cooked:
143             self._cook()
144         if not self._v_errors:
145             if not self.expand:
146                 return self._text
147             try:
148                 return self.pt_render(source=1)
149             except:
150                 return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
151                         (self._error_start, "%s: %s" % sys.exc_info()[:2],
152                          self._text) )
153                                   
154         return ('%s\n %s\n-->\n%s' % (self._error_start,
155                                       join(self._v_errors, '\n '),
156                                       self._text))
158     def _cook(self):
159         """Compile the TAL and METAL statments.
161         Cooking must not fail due to compilation errors in templates.
162         """
163         source_file = self.pt_source_file()
164         if self.html():
165             gen = TALGenerator(getEngine(), xml=0, source_file=source_file)
166             parser = HTMLTALParser(gen)
167         else:
168             gen = TALGenerator(getEngine(), source_file=source_file)
169             parser = TALParser(gen)
171         self._v_errors = ()
172         try:
173             parser.parseString(self._text)
174             self._v_program, self._v_macros = parser.getCode()
175         except:
176             self._v_errors = ["Compilation failed",
177                               "%s: %s" % sys.exc_info()[:2]]
178         self._v_warnings = parser.getWarnings()
179         self._v_cooked = 1
181     def html(self):
182         if not hasattr(getattr(self, 'aq_base', self), 'is_html'):
183             return self.content_type == 'text/html'
184         return self.is_html
186 class _ModuleImporter:
187     def __getitem__(self, module):
188         mod = __import__(module)
189         path = split(module, '.')
190         for name in path[1:]:
191             mod = getattr(mod, name)
192         return mod
194 ModuleImporter = _ModuleImporter()
196 class PTRuntimeError(RuntimeError):
197     '''The Page Template has template errors that prevent it from rendering.'''
198     pass
201 class PageTemplateTracebackSupplement:
202     #__implements__ = ITracebackSupplement
204     def __init__(self, pt):
205         self.object = pt
206         w = pt.pt_warnings()
207         e = pt.pt_errors()
208         if e:
209             w = list(w) + list(e)
210         self.warnings = w