Code

Add config-option "nosy" to messages_to_author setting in [nosy] section
[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 # Modified for Roundup:
14
15 # 1. changed imports to import from roundup.cgi
16 # 2. removed use of ExtensionClass
17 # 3. removed use of ComputedAttribute
18 """Page Template module
20 HTML- and XML-based template objects using TAL, TALES, and METAL.
21 """
23 __version__='$Revision: 1.5 $'[11:-2]
25 import sys
27 from roundup.cgi.TAL.TALParser import TALParser
28 from roundup.cgi.TAL.HTMLTALParser import HTMLTALParser
29 from roundup.cgi.TAL.TALGenerator import TALGenerator
30 # Do not use cStringIO here!  It's not unicode aware. :(
31 from roundup.cgi.TAL.TALInterpreter import TALInterpreter, FasterStringIO
32 from Expressions import getEngine
35 class PageTemplate:
36     "Page Templates using TAL, TALES, and METAL"
38     content_type = 'text/html'
39     expand = 0
40     _v_errors = ()
41     _v_warnings = ()
42     _v_program = None
43     _v_macros = None
44     _v_cooked = 0
45     id = '(unknown)'
46     _text = ''
47     _error_start = '<!-- Page Template Diagnostics'
49     def StringIO(self):
50         # Third-party products wishing to provide a full Unicode-aware
51         # StringIO can do so by monkey-patching this method.
52         return FasterStringIO()
54     def pt_edit(self, text, content_type):
55         if content_type:
56             self.content_type = str(content_type)
57         if hasattr(text, 'read'):
58             text = text.read()
59         self.write(text)
61     def pt_getContext(self):
62         c = {'template': self,
63              'options': {},
64              'nothing': None,
65              'request': None,
66              'modules': ModuleImporter,
67              }
68         parent = getattr(self, 'aq_parent', None)
69         if parent is not None:
70             c['here'] = parent
71             c['container'] = self.aq_inner.aq_parent
72             while parent is not None:
73                 self = parent
74                 parent = getattr(self, 'aq_parent', None)
75             c['root'] = self
76         return c
78     def pt_render(self, source=0, extra_context={}):
79         """Render this Page Template"""
80         if not self._v_cooked:
81             self._cook()
83         __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
85         if self._v_errors:
86             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
87         output = self.StringIO()
88         c = self.pt_getContext()
89         c.update(extra_context)
91         TALInterpreter(self._v_program, self._v_macros,
92                        getEngine().getContext(c),
93                        output,
94                        tal=not source, strictinsert=0)()
95         return output.getvalue()
97     def __call__(self, *args, **kwargs):
98         if not kwargs.has_key('args'):
99             kwargs['args'] = args
100         return self.pt_render(extra_context={'options': kwargs})
102     def pt_errors(self):
103         if not self._v_cooked:
104             self._cook()
105         err = self._v_errors
106         if err:
107             return err
108         if not self.expand: return
109         try:
110             self.pt_render(source=1)
111         except:
112             return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
114     def pt_warnings(self):
115         if not self._v_cooked:
116             self._cook()
117         return self._v_warnings
119     def pt_macros(self):
120         if not self._v_cooked:
121             self._cook()
122         __traceback_supplement__ = (PageTemplateTracebackSupplement, self)
123         if self._v_errors:
124             raise PTRuntimeError, 'Page Template %s has errors.' % self.id
125         return self._v_macros
127     def __getattr__(self, name):
128         if name == 'macros':
129             return self.pt_macros()
130         raise AttributeError, name
132     def pt_source_file(self):
133         return None  # Unknown.
135     def write(self, text):
136         assert type(text) is type('')
137         if text[:len(self._error_start)] == self._error_start:
138             errend = text.find('-->')
139             if errend >= 0:
140                 text = text[errend + 4:]
141         if self._text != text:
142             self._text = text
143         self._cook()
145     def read(self):
146         self._cook_check()
147         if not self._v_errors:
148             if not self.expand:
149                 return self._text
150             try:
151                 return self.pt_render(source=1)
152             except:
153                 return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
154                         (self._error_start, "%s: %s" % sys.exc_info()[:2],
155                          self._text) )
157         return ('%s\n %s\n-->\n%s' % (self._error_start,
158                                       '\n '.join(self._v_errors),
159                                       self._text))
161     def _cook_check(self):
162         if not self._v_cooked:
163             self._cook()
165     def _cook(self):
166         """Compile the TAL and METAL statments.
168         Cooking must not fail due to compilation errors in templates.
169         """
170         source_file = self.pt_source_file()
171         if self.html():
172             gen = TALGenerator(getEngine(), xml=0, source_file=source_file)
173             parser = HTMLTALParser(gen)
174         else:
175             gen = TALGenerator(getEngine(), source_file=source_file)
176             parser = TALParser(gen)
178         self._v_errors = ()
179         try:
180             parser.parseString(self._text)
181             self._v_program, self._v_macros = parser.getCode()
182         except:
183             self._v_errors = ["Compilation failed",
184                               "%s: %s" % sys.exc_info()[:2]]
185         self._v_warnings = parser.getWarnings()
186         self._v_cooked = 1
188     def html(self):
189         if not hasattr(getattr(self, 'aq_base', self), 'is_html'):
190             return self.content_type == 'text/html'
191         return self.is_html
193 class _ModuleImporter:
194     def __getitem__(self, module):
195         mod = __import__(module)
196         path = module.split('.')
197         for name in path[1:]:
198             mod = getattr(mod, name)
199         return mod
201 ModuleImporter = _ModuleImporter()
203 class PTRuntimeError(RuntimeError):
204     '''The Page Template has template errors that prevent it from rendering.'''
205     pass
208 class PageTemplateTracebackSupplement:
209     #__implements__ = ITracebackSupplement
211     def __init__(self, pt):
212         self.object = pt
213         w = pt.pt_warnings()
214         e = pt.pt_errors()
215         if e:
216             w = list(w) + list(e)
217         self.warnings = w