Code

ddb097ca2b87435c7eb66b46e9911cfb3d46ada7
[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 """
23 __docformat__ = 'restructuredtext'
25 __version__='$Revision: 1.4 $'[11:-2]
27 import sys
29 from roundup.cgi.TAL.TALParser import TALParser
30 from roundup.cgi.TAL.HTMLTALParser import HTMLTALParser
31 from roundup.cgi.TAL.TALGenerator import TALGenerator
32 from roundup.cgi.TAL.TALInterpreter import TALInterpreter
33 from Expressions import getEngine
34 from string import join, strip, rstrip, split, replace, lower, find
35 from cStringIO import StringIO
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 pt_edit(self, text, content_type):
52         if content_type:
53             self.content_type = str(content_type)
54         if hasattr(text, 'read'):
55             text = text.read()
56         self.write(text)
58     def pt_getContext(self):
59         c = {'template': self,
60              'options': {},
61              'nothing': None,
62              'request': None,
63              'modules': ModuleImporter,
64              }
65         parent = getattr(self, 'aq_parent', None)
66         if parent is not None:
67             c['here'] = parent
68             c['container'] = self.aq_inner.aq_parent
69             while parent is not None:
70                 self = parent
71                 parent = getattr(self, 'aq_parent', None)
72             c['root'] = self
73         return c
74     
75     def pt_render(self, source=0, extra_context={}):
76         """Render this Page Template"""
77         if not self._v_cooked:
78             self._cook()
80         __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
82         if self._v_errors:
83             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
84         output = StringIO()
85         c = self.pt_getContext()
86         c.update(extra_context)
88         TALInterpreter(self._v_program, self._v_macros,
89                        getEngine().getContext(c),
90                        output,
91                        tal=not source, strictinsert=0)()
92         return output.getvalue()
94     def __call__(self, *args, **kwargs):
95         if not kwargs.has_key('args'):
96             kwargs['args'] = args
97         return self.pt_render(extra_context={'options': kwargs})
99     def pt_errors(self):
100         if not self._v_cooked:
101             self._cook()
102         err = self._v_errors
103         if err:
104             return err
105         if not self.expand: return
106         try:
107             self.pt_render(source=1)
108         except:
109             return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
110         
111     def pt_warnings(self):
112         if not self._v_cooked:
113             self._cook()
114         return self._v_warnings
116     def pt_macros(self):
117         if not self._v_cooked:
118             self._cook()
119         __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
120         if self._v_errors:
121             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
122         return self._v_macros
124     def __getattr__(self, name):
125         if name == 'macros':
126             return self.pt_macros()
127         raise AttributeError, name
129     def pt_source_file(self):
130         return None  # Unknown.
132     def write(self, text):
133         assert type(text) is type('')
134         if text[:len(self._error_start)] == self._error_start:
135             errend = find(text, '-->')
136             if errend >= 0:
137                 text = text[errend + 4:]
138         if self._text != text:
139             self._text = text
140         self._cook()
142     def read(self):
143         if not self._v_cooked:
144             self._cook()
145         if not self._v_errors:
146             if not self.expand:
147                 return self._text
148             try:
149                 return self.pt_render(source=1)
150             except:
151                 return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
152                         (self._error_start, "%s: %s" % sys.exc_info()[:2],
153                          self._text) )
154                                   
155         return ('%s\n %s\n-->\n%s' % (self._error_start,
156                                       join(self._v_errors, '\n '),
157                                       self._text))
159     def _cook(self):
160         """Compile the TAL and METAL statments.
162         Cooking must not fail due to compilation errors in templates.
163         """
164         source_file = self.pt_source_file()
165         if self.html():
166             gen = TALGenerator(getEngine(), xml=0, source_file=source_file)
167             parser = HTMLTALParser(gen)
168         else:
169             gen = TALGenerator(getEngine(), source_file=source_file)
170             parser = TALParser(gen)
172         self._v_errors = ()
173         try:
174             parser.parseString(self._text)
175             self._v_program, self._v_macros = parser.getCode()
176         except:
177             self._v_errors = ["Compilation failed",
178                               "%s: %s" % sys.exc_info()[:2]]
179         self._v_warnings = parser.getWarnings()
180         self._v_cooked = 1
182     def html(self):
183         if not hasattr(getattr(self, 'aq_base', self), 'is_html'):
184             return self.content_type == 'text/html'
185         return self.is_html
187 class _ModuleImporter:
188     def __getitem__(self, module):
189         mod = __import__(module)
190         path = split(module, '.')
191         for name in path[1:]:
192             mod = getattr(mod, name)
193         return mod
195 ModuleImporter = _ModuleImporter()
197 class PTRuntimeError(RuntimeError):
198     '''The Page Template has template errors that prevent it from rendering.'''
199     pass
202 class PageTemplateTracebackSupplement:
203     #__implements__ = ITracebackSupplement
205     def __init__(self, pt):
206         self.object = pt
207         w = pt.pt_warnings()
208         e = pt.pt_errors()
209         if e:
210             w = list(w) + list(e)
211         self.warnings = w