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.3 $'[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
36 class PageTemplate:
37 "Page Templates using TAL, TALES, and METAL"
39 content_type = 'text/html'
40 expand = 0
41 _v_errors = ()
42 _v_warnings = ()
43 _v_program = None
44 _v_macros = None
45 _v_cooked = 0
46 id = '(unknown)'
47 _text = ''
48 _error_start = '<!-- Page Template Diagnostics'
50 def pt_edit(self, text, content_type):
51 if content_type:
52 self.content_type = str(content_type)
53 if hasattr(text, 'read'):
54 text = text.read()
55 self.write(text)
57 def pt_getContext(self):
58 c = {'template': self,
59 'options': {},
60 'nothing': None,
61 'request': None,
62 'modules': ModuleImporter,
63 }
64 parent = getattr(self, 'aq_parent', None)
65 if parent is not None:
66 c['here'] = parent
67 c['container'] = self.aq_inner.aq_parent
68 while parent is not None:
69 self = parent
70 parent = getattr(self, 'aq_parent', None)
71 c['root'] = self
72 return c
74 def pt_render(self, source=0, extra_context={}):
75 """Render this Page Template"""
76 if not self._v_cooked:
77 self._cook()
79 __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
81 if self._v_errors:
82 raise PTRuntimeError, 'Page Template %s has errors.' % self.id
83 output = StringIO()
84 c = self.pt_getContext()
85 c.update(extra_context)
87 TALInterpreter(self._v_program, self._v_macros,
88 getEngine().getContext(c),
89 output,
90 tal=not source, strictinsert=0)()
91 return output.getvalue()
93 def __call__(self, *args, **kwargs):
94 if not kwargs.has_key('args'):
95 kwargs['args'] = args
96 return self.pt_render(extra_context={'options': kwargs})
98 def pt_errors(self):
99 if not self._v_cooked:
100 self._cook()
101 err = self._v_errors
102 if err:
103 return err
104 if not self.expand: return
105 try:
106 self.pt_render(source=1)
107 except:
108 return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
110 def pt_warnings(self):
111 if not self._v_cooked:
112 self._cook()
113 return self._v_warnings
115 def pt_macros(self):
116 if not self._v_cooked:
117 self._cook()
118 __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
119 if self._v_errors:
120 raise PTRuntimeError, 'Page Template %s has errors.' % self.id
121 return self._v_macros
123 def __getattr__(self, name):
124 if name == 'macros':
125 return self.pt_macros()
126 raise AttributeError, name
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) )
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