Code

moved
[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.
16 """
18 __version__='$Revision: 1.1 $'[11:-2]
20 import sys
22 from roundup.cgi.TAL.TALParser import TALParser
23 from roundup.cgi.TAL.HTMLTALParser import HTMLTALParser
24 from roundup.cgi.TAL.TALGenerator import TALGenerator
25 from roundup.cgi.TAL.TALInterpreter import TALInterpreter
26 from Expressions import getEngine
27 from string import join, strip, rstrip, split, replace, lower, find
28 from cStringIO import StringIO
29 from ComputedAttribute import ComputedAttribute
31 class PageTemplate:
32     "Page Templates using TAL, TALES, and METAL"
33      
34     content_type = 'text/html'
35     expand = 0
36     _v_errors = ()
37     _v_warnings = ()
38     _v_program = None
39     _v_macros = None
40     _v_cooked = 0
41     id = '(unknown)'
42     _text = ''
43     _error_start = '<!-- Page Template Diagnostics'
45     def macros(self):
46         return self.pt_macros()
47     macros = ComputedAttribute(macros, 1)
49     def pt_edit(self, text, content_type):
50         if content_type:
51             self.content_type = str(content_type)
52         if hasattr(text, 'read'):
53             text = text.read()
54         self.write(text)
56     def pt_getContext(self):
57         c = {'template': self,
58              'options': {},
59              'nothing': None,
60              'request': None,
61              'modules': ModuleImporter,
62              }
63         parent = getattr(self, 'aq_parent', None)
64         if parent is not None:
65             c['here'] = parent
66             c['container'] = self.aq_inner.aq_parent
67             while parent is not None:
68                 self = parent
69                 parent = getattr(self, 'aq_parent', None)
70             c['root'] = self
71         return c
72     
73     def pt_render(self, source=0, extra_context={}):
74         """Render this Page Template"""
75         if not self._v_cooked:
76             self._cook()
78         __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
80         if self._v_errors:
81             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
82         output = StringIO()
83         c = self.pt_getContext()
84         c.update(extra_context)
86         TALInterpreter(self._v_program, self._v_macros,
87                        getEngine().getContext(c),
88                        output,
89                        tal=not source, strictinsert=0)()
90         return output.getvalue()
92     def __call__(self, *args, **kwargs):
93         if not kwargs.has_key('args'):
94             kwargs['args'] = args
95         return self.pt_render(extra_context={'options': kwargs})
97     def pt_errors(self):
98         if not self._v_cooked:
99             self._cook()
100         err = self._v_errors
101         if err:
102             return err
103         if not self.expand: return
104         try:
105             self.pt_render(source=1)
106         except:
107             return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
108         
109     def pt_warnings(self):
110         if not self._v_cooked:
111             self._cook()
112         return self._v_warnings
114     def pt_macros(self):
115         if not self._v_cooked:
116             self._cook()
117         if self._v_errors:
118             __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
119             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
120         return self._v_macros
122     def pt_source_file(self):
123         return None  # Unknown.
125     def write(self, text):
126         assert type(text) is type('')
127         if text[:len(self._error_start)] == self._error_start:
128             errend = find(text, '-->')
129             if errend >= 0:
130                 text = text[errend + 4:]
131         if self._text != text:
132             self._text = text
133         self._cook()
135     def read(self):
136         if not self._v_cooked:
137             self._cook()
138         if not self._v_errors:
139             if not self.expand:
140                 return self._text
141             try:
142                 return self.pt_render(source=1)
143             except:
144                 return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
145                         (self._error_start, "%s: %s" % sys.exc_info()[:2],
146                          self._text) )
147                                   
148         return ('%s\n %s\n-->\n%s' % (self._error_start,
149                                       join(self._v_errors, '\n '),
150                                       self._text))
152     def _cook(self):
153         """Compile the TAL and METAL statments.
155         Cooking must not fail due to compilation errors in templates.
156         """
157         source_file = self.pt_source_file()
158         if self.html():
159             gen = TALGenerator(getEngine(), xml=0, source_file=source_file)
160             parser = HTMLTALParser(gen)
161         else:
162             gen = TALGenerator(getEngine(), source_file=source_file)
163             parser = TALParser(gen)
165         self._v_errors = ()
166         try:
167             parser.parseString(self._text)
168             self._v_program, self._v_macros = parser.getCode()
169         except:
170             self._v_errors = ["Compilation failed",
171                               "%s: %s" % sys.exc_info()[:2]]
172         self._v_warnings = parser.getWarnings()
173         self._v_cooked = 1
175     def html(self):
176         if not hasattr(getattr(self, 'aq_base', self), 'is_html'):
177             return self.content_type == 'text/html'
178         return self.is_html
180 class _ModuleImporter:
181     def __getitem__(self, module):
182         mod = __import__(module)
183         path = split(module, '.')
184         for name in path[1:]:
185             mod = getattr(mod, name)
186         return mod
188 ModuleImporter = _ModuleImporter()
190 class PTRuntimeError(RuntimeError):
191     '''The Page Template has template errors that prevent it from rendering.'''
192     pass
195 class PageTemplateTracebackSupplement:
196     #__implements__ = ITracebackSupplement
198     def __init__(self, pt):
199         self.object = pt
200         w = pt.pt_warnings()
201         e = pt.pt_errors()
202         if e:
203             w = list(w) + list(e)
204         self.warnings = w