• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Zope / 3956162881

pending completion
3956162881

push

github

Michael Howitz
Update to deprecation warning free releases.

4401 of 7036 branches covered (62.55%)

Branch coverage included in aggregate %.

27161 of 31488 relevant lines covered (86.26%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.32
/src/Products/PageTemplates/PageTemplateFile.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (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

14
import os
1✔
15
from logging import getLogger
1✔
16

17
from AccessControl.class_init import InitializeClass
1✔
18
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
19
from AccessControl.SecurityManagement import getSecurityManager
1✔
20
from Acquisition import aq_get
1✔
21
from Acquisition import aq_inner
1✔
22
from Acquisition import aq_parent
1✔
23
from App.Common import package_home
1✔
24
from App.config import getConfiguration
1✔
25
from ComputedAttribute import ComputedAttribute
1✔
26
from OFS.SimpleItem import SimpleItem
1✔
27
from OFS.Traversable import Traversable
1✔
28
from Products.PageTemplates.Expressions import SecureModuleImporter
1✔
29
from Products.PageTemplates.PageTemplate import PageTemplate
1✔
30
from Products.PageTemplates.utils import encodingFromXMLPreamble
1✔
31
from Shared.DC.Scripts.Script import Script
1✔
32
from Shared.DC.Scripts.Signature import FuncCode
1✔
33
from zope.contenttype import guess_content_type
1✔
34
from zope.pagetemplate.pagetemplatefile import DEFAULT_ENCODING
1✔
35
from zope.pagetemplate.pagetemplatefile import XML_PREFIX_MAX_LENGTH
1✔
36
from zope.pagetemplate.pagetemplatefile import meta_pattern
1✔
37
from zope.pagetemplate.pagetemplatefile import sniff_type
1✔
38

39

40
LOG = getLogger('PageTemplateFile')
1✔
41

42

43
def guess_type(filename, body):
1✔
44
    # check for XML ourself since guess_content_type can't
45
    # detect text/xml  if 'filename' won't end with .xml
46
    # XXX: fix this in zope.contenttype
47

48
    if body.startswith(b'<?xml') or filename.lower().endswith('.xml'):
1✔
49
        return 'text/xml'
1✔
50

51
    content_type, ignored_encoding = guess_content_type(filename, body)
1✔
52
    if content_type in ('text/html', 'text/xml'):
1✔
53
        return content_type
1✔
54
    return sniff_type(body) or 'text/html'
1✔
55

56

57
# REFACT: Make this a subclass of
58
# zope.pagetemplate.pagetemplatefile.PageTemplateFile
59
# That class has been forked off of this code and now we have duplication.
60
# They already share a common superclass
61
# zope.pagetemplate.pagetemplate.PageTemplate
62
class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable):
1✔
63
    """Zope 2 implementation of a PageTemplate loaded from a file."""
64

65
    meta_type = 'Page Template (File)'
1✔
66

67
    __code__ = FuncCode((), 0)
1✔
68
    __defaults__ = None
1✔
69
    _v_last_read = 0
1✔
70

71
    # needed by AccessControl.class_init.InitializeClass
72
    _need__name__ = 1
1✔
73

74
    _default_bindings = {'name_subpath': 'traverse_subpath'}
1✔
75

76
    security = ClassSecurityInfo()
1✔
77
    security.declareProtected(  # NOQA: D001
1✔
78
        'View management screens', 'read', 'document_src')
79

80
    def __init__(
1✔
81
        self, filename, _prefix=None, encoding=DEFAULT_ENCODING, **kw
82
    ):
83
        name = kw.pop('__name__', None)
1✔
84
        self.encoding = encoding
1✔
85
        basepath, ext = os.path.splitext(filename)
1✔
86

87
        if name:
1✔
88
            self.id = self.__name__ = name
1✔
89
            self._need__name__ = 0
1✔
90
        else:
91
            self.id = self.__name__ = os.path.basename(basepath)
1✔
92

93
        if _prefix:
1✔
94
            if isinstance(_prefix, str):
1!
95
                filename = os.path.join(_prefix, filename)
×
96
            else:
97
                filename = os.path.join(package_home(_prefix), filename)
1✔
98

99
        if not ext:
1✔
100
            filename = filename + '.zpt'
1✔
101

102
        self.filename = filename
1✔
103

104
    def pt_getContext(self):
1✔
105
        root = None
1✔
106
        meth = aq_get(self, 'getPhysicalRoot', None)
1✔
107
        if callable(meth):
1✔
108
            root = meth()
1✔
109
        context = self._getContext()
1✔
110
        c = {'template': self,
1✔
111
             'here': context,
112
             'context': context,
113
             'container': self._getContainer(),
114
             'nothing': None,
115
             'options': {},
116
             'root': root,
117
             'request': aq_get(root, 'REQUEST', None),
118
             'modules': SecureModuleImporter,
119
             }
120
        return c
1✔
121

122
    def _exec(self, bound_names, args, kw):
1✔
123
        """Call a Page Template"""
124
        self._cook_check()
1✔
125
        if 'args' not in kw:
1!
126
            kw['args'] = args
1✔
127
        bound_names['options'] = kw
1✔
128

129
        request = aq_get(self, 'REQUEST', None)
1✔
130
        if request is not None:
1!
131
            response = request.response
1✔
132
            if 'content-type' not in response.headers:
1✔
133
                response.setHeader('content-type', self.content_type)
1✔
134

135
        # Execute the template in a new security context.
136
        security = getSecurityManager()
1✔
137
        bound_names['user'] = security.getUser()
1✔
138
        security.addContext(self)
1✔
139

140
        try:
1✔
141
            context = self.pt_getContext()
1✔
142
            context.update(bound_names)
1✔
143
            return self.pt_render(extra_context=bound_names)
1✔
144
        finally:
145
            security.removeContext(self)
1✔
146

147
    def pt_macros(self):
1✔
148
        self._cook_check()
×
149
        return PageTemplate.pt_macros(self)
×
150

151
    def pt_source_file(self):
1✔
152
        """Returns a file name to be compiled into the TAL code."""
153
        return self.__name__  # Don't reveal filesystem paths
1✔
154

155
    def _cook_check(self):
1✔
156
        if self._v_last_read and not getConfiguration().debug_mode:
1✔
157
            return
1✔
158
        __traceback_info__ = self.filename
1✔
159
        try:
1✔
160
            mtime = os.path.getmtime(self.filename)
1✔
161
        except OSError:
×
162
            mtime = 0
×
163
        if self._v_program is not None and mtime == self._v_last_read:
1!
164
            return
×
165
        text, type_ = self._read_file()
1✔
166
        self.pt_edit(text, type_)
1✔
167
        self._cook()
1✔
168
        if self._v_errors:
1!
169
            LOG.error('Error in template %s' % '\n'.join(self._v_errors))
×
170
            return
×
171
        self._v_last_read = mtime
1✔
172

173
    def _prepare_html(self, text):
1✔
174
        match = meta_pattern.search(text)
1✔
175
        if match is not None:
1!
176
            type_, encoding = (x.decode(self.encoding) for x in match.groups())
×
177
            # TODO: Shouldn't <meta>/<?xml?> stripping
178
            # be in PageTemplate.__call__()?
179
            text = meta_pattern.sub(b"", text)
×
180
        else:
181
            type_ = None
1✔
182
            encoding = self.encoding
1✔
183
        text = text.decode(encoding)
1✔
184
        return text, type_
1✔
185

186
    def _prepare_xml(self, text):
1✔
187
        if not isinstance(text, str):
1!
188
            encoding = encodingFromXMLPreamble(text, default=self.encoding)
1✔
189
            text = text.decode(encoding)
1✔
190
        return text, 'text/xml'
1✔
191

192
    def _read_file(self):
1✔
193
        __traceback_info__ = self.filename
1✔
194
        with open(self.filename, "rb") as f:
1✔
195
            text = f.read(XML_PREFIX_MAX_LENGTH)
1✔
196
            type_ = sniff_type(text)
1✔
197
            text += f.read()
1✔
198
        if type_ != "text/xml":
1✔
199
            text, type_ = self._prepare_html(text)
1✔
200
        else:
201
            text, type_ = self._prepare_xml(text)
1✔
202
        f.close()
1✔
203
        return text, type_
1✔
204

205
    def document_src(self, REQUEST=None, RESPONSE=None):
1✔
206
        """Return expanded document source."""
207

208
        if RESPONSE is not None:
×
209
            # Since _cook_check() can cause self.content_type to change,
210
            # we have to make sure we call it before setting the
211
            # Content-Type header.
212
            self._cook_check()
×
213
            RESPONSE.setHeader('Content-Type', 'text/plain')
×
214
        return self.read()
×
215

216
    def _get__roles__(self):
1✔
217
        imp = getattr(aq_parent(aq_inner(self)),
1✔
218
                      '%s__roles__' % self.__name__)
219
        if hasattr(imp, '__of__'):
1!
220
            return imp.__of__(self)
×
221
        return imp
1✔
222

223
    __roles__ = ComputedAttribute(_get__roles__, 1)
1✔
224

225
    def getOwner(self, info=0):
1✔
226
        """Gets the owner of the executable object.
227

228
        This method is required of all objects that go into
229
        the security context stack.  Since this object came from the
230
        filesystem, it is owned by no one managed by Zope.
231
        """
232
        return None
1✔
233

234
    def __getstate__(self):
1✔
235
        from ZODB.POSException import StorageError
×
236
        raise StorageError("Instance of AntiPersistent class %s "
×
237
                           "cannot be stored." % self.__class__.__name__)
238

239

240
InitializeClass(PageTemplateFile)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc