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

zopefoundation / Products.CMFCore / 6246931310

20 Sep 2023 09:54AM UTC coverage: 86.008% (-0.3%) from 86.266%
6246931310

Pull #131

github

mauritsvanrees
gha: don't need setup-python on 27 as we use the 27 container.
Pull Request #131: Make decodeFolderFilter and encodeFolderFilter non-public.

2466 of 3689 branches covered (0.0%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

17297 of 19289 relevant lines covered (89.67%)

0.9 hits per line

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

84.51
/src/Products/CMFCore/FSPageTemplate.py
1
##############################################################################
2
#
3
# Copyright (c) 2001 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
""" Customizable page templates that come from the filesystem.
1✔
14
"""
15

16
import re
1✔
17

18
import six
1✔
19
from six import get_unbound_function
1✔
20

21
from AccessControl.class_init import InitializeClass
1✔
22
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
23
from AccessControl.SecurityManagement import getSecurityManager
1✔
24
from App.special_dtml import DTMLFile
1✔
25
from Products.PageTemplates.PageTemplate import PageTemplate
1✔
26
from Products.PageTemplates.utils import charsetFromMetaEquiv
1✔
27
from Products.PageTemplates.utils import encodingFromXMLPreamble
1✔
28
from Products.PageTemplates.ZopePageTemplate import Src
1✔
29
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
1✔
30
from Products.PageTemplates.ZopePageTemplate import preferred_encodings
1✔
31
from Shared.DC.Scripts.Script import Script
1✔
32

33
from .DirectoryView import registerFileExtension
1✔
34
from .DirectoryView import registerMetaType
1✔
35
from .FSObject import FSObject
1✔
36
from .permissions import FTPAccess
1✔
37
from .permissions import View
1✔
38
from .permissions import ViewManagementScreens
1✔
39
from .utils import HAS_ZSERVER
1✔
40
from .utils import _checkConditionalGET
1✔
41
from .utils import _dtmldir
1✔
42
from .utils import _setCacheHeaders
1✔
43

44

45
xml_detect_re = re.compile(
1✔
46
    br'^\s*<\?xml\s+(?:[^>]*?encoding=["\']([^"\'>]+))?')
47
charset_re = re.compile(r'charset.*?=.*?(?P<charset>[\w\-]*)',
1✔
48
                        re.I | re.M | re.S)
49
_marker = object()
1✔
50

51

52
class FSPageTemplate(FSObject, Script, PageTemplate):
1✔
53

54
    """Wrapper for Page Template.
55
    """
56

57
    meta_type = 'Filesystem Page Template'
1✔
58
    zmi_icon = 'far fa-file-code'
1✔
59
    _owner = None  # Unowned
1✔
60

61
    manage_options = (
1✔
62
        {'label': 'Customize', 'action': 'manage_main'},)
63

64
    security = ClassSecurityInfo()
1✔
65
    security.declareObjectProtected(View)
1✔
66

67
    security.declareProtected(ViewManagementScreens,  # NOQA: flake8: D001
1✔
68
                              'manage_main')
69
    manage_main = DTMLFile('custpt', _dtmldir)
1✔
70

71
    # Declare security for unprotected PageTemplate methods.
72
    security.declarePrivate('pt_edit', 'write')  # NOQA: flake8: D001
1✔
73

74
    def __init__(self, id, filepath, fullname=None, properties=None):
1✔
75
        FSObject.__init__(self, id, filepath, fullname, properties)
1✔
76
        self.ZBindings_edit(self._default_bindings)
1✔
77

78
    def _createZODBClone(self):
1✔
79
        """Create a ZODB (editable) equivalent of this object."""
80
        obj = ZopePageTemplate(self.getId(), self._text, self.content_type)
1✔
81
        obj.expand = 0
1✔
82
        obj.write(self.read())
1✔
83
        return obj
1✔
84

85
#    def ZCacheable_isCachingEnabled(self):
86
#        return 0
87

88
    def _readFile(self, reparse):
1✔
89
        """Read the data from the filesystem.
90
        """
91
        if reparse:
1✔
92
            if six.PY2:
1!
93
                # not 'rb', as this is a text file!
94
                file = open(self._filepath, 'rU')
×
95
            else:
96
                file = open(self._filepath, 'br')
1✔
97
            try:
1✔
98
                data = file.read()
1✔
99
                if not six.PY2:
1!
100
                    data = data.replace(b'\r\n', b'\n').replace(b'\r', b'\n')
1✔
101
            finally:
102
                file.close()
1✔
103

104
            # If we already have a content_type set it must come from a
105
            # .metadata file and we should always honor that. The content
106
            # type is initialized as text/html by default, so we only
107
            # attempt further detection if the default is encountered.
108
            # One previous misbehavior remains: It is not possible to
109
            # force a text/html type if parsing detects it as XML.
110
            encoding = None
1✔
111
            preferred = preferred_encodings[:]
1✔
112

113
            if getattr(self, 'content_type', 'text/html') == 'text/html':
1✔
114
                xml_info = xml_detect_re.match(data)
1✔
115
                if xml_info:
1✔
116
                    # Smells like xml
117
                    # set "content_type" from the XML declaration
118
                    if xml_info.group(1):
1✔
119
                        if six.PY3:
1!
120
                            encoding = xml_info.group(1).decode('ascii')
1✔
121
                        else:
122
                            encoding = xml_info.group(1)
×
123
                    else:
124
                        encoding = 'utf-8'
1✔
125
                    self.content_type = 'text/xml; charset=%s' % encoding
1✔
126

127
            if not isinstance(data, six.text_type):
1!
128
                if encoding is None:
1✔
129
                    charset = getattr(self, 'charset', None)
1✔
130

131
                    if charset is None:
1✔
132
                        if self.content_type.startswith('text/html'):
1✔
133
                            mo = charset_re.search(self.content_type)
1✔
134
                            if mo:
1✔
135
                                charset = mo.group(1).lower()
1✔
136

137
                            if charset is None:
1✔
138
                                charset = charsetFromMetaEquiv(data)
1✔
139

140
                        elif self.content_type.startswith('text/xml'):
1!
141
                            charset = encodingFromXMLPreamble(data)
1✔
142

143
                        else:
144
                            raise ValueError('Unsupported content_type: %s' %
×
145
                                             self.content_type)
146

147
                    if charset is not None:
1✔
148
                        preferred.insert(0, charset)
1✔
149

150
                else:
151
                    preferred.insert(0, encoding)
1✔
152

153
                for enc in preferred:
1!
154
                    try:
1✔
155
                        data = six.text_type(data, enc)
1✔
156
                        if isinstance(data, six.text_type):
1!
157
                            break
1✔
158
                    except UnicodeDecodeError:
×
159
                        continue
×
160
                else:
161
                    data = six.text_type(data)
×
162

163
            self.write(data)
1✔
164

165
    @security.private
1✔
166
    def read(self):
1✔
167
        # Tie in on an opportunity to auto-update
168
        self._updateFromFS()
1✔
169
        return FSPageTemplate.inheritedAttribute('read')(self)
1✔
170

171
    # The following is mainly taken from ZopePageTemplate.py
172

173
    expand = 0
1✔
174
    output_encoding = 'utf-8'
1✔
175

176
    __defaults__ = None
1✔
177
    __code__ = ZopePageTemplate.__code__
1✔
178
    _default_bindings = ZopePageTemplate._default_bindings
1✔
179

180
    security.declareProtected(View, '__call__')  # NOQA: flake8: D001
1✔
181

182
    def pt_macros(self):
1✔
183
        # Tie in on an opportunity to auto-reload
184
        self._updateFromFS()
×
185
        return FSPageTemplate.inheritedAttribute('pt_macros')(self)
×
186

187
    def pt_render(self, source=0, extra_context={}):
1✔
188
        self._updateFromFS()  # Make sure the template has been loaded.
1✔
189

190
        if not source:
1!
191
            # If we have a conditional get, set status 304 and return
192
            # no content
193
            if _checkConditionalGET(self, extra_context):
1✔
194
                _setCacheHeaders(self, extra_context)
1✔
195
                return ''
1✔
196

197
        result = FSPageTemplate.inheritedAttribute('pt_render')(
1✔
198
                                self, source, extra_context)
199
        if not source:
1!
200
            _setCacheHeaders(self, extra_context)
1✔
201
        return result
1✔
202

203
    @security.protected(ViewManagementScreens)
1✔
204
    def pt_source_file(self):
1✔
205

206
        """ Return a file name to be compiled into the TAL code.
207
        """
208
        return 'file:%s' % self._filepath
1✔
209

210
    security.declarePrivate('_ZPT_exec')  # NOQA: flake8: D001
1✔
211
    _ZPT_exec = get_unbound_function(ZopePageTemplate._exec)
1✔
212

213
    @security.private
1✔
214
    def _exec(self, bound_names, args, kw):
1✔
215
        """Call a FSPageTemplate"""
216
        try:
1✔
217
            response = self.REQUEST.RESPONSE
1✔
218
        except AttributeError:
×
219
            response = None
×
220
        # Read file first to get a correct content_type default value.
221
        self._updateFromFS()
1✔
222

223
        if 'args' not in kw:
1!
224
            kw['args'] = args
1✔
225
        bound_names['options'] = kw
1✔
226

227
        try:
1✔
228
            response = self.REQUEST.RESPONSE
1✔
229
            if 'content-type' not in response.headers:
1✔
230
                response.setHeader('content-type', self.content_type)
1✔
231
        except AttributeError:
×
232
            pass
×
233

234
        security = getSecurityManager()
1✔
235
        bound_names['user'] = security.getUser()
1✔
236

237
        # Retrieve the value from the cache.
238
        keyset = None
1✔
239
        if self.ZCacheable_isCachingEnabled():
1!
240
            # Prepare a cache key.
241
            keyset = {
×
242
                      # Why oh why?
243
                      # All this code is cut and paste
244
                      # here to make sure that we
245
                      # dont call _getContext and hence can't cache
246
                      # Annoying huh?
247
                      'here': self.aq_parent.getPhysicalPath(),
248
                      'bound_names': bound_names}
249
            result = self.ZCacheable_get(keywords=keyset)
×
250
            if result is not None:
×
251
                # Got a cached value.
252
                return result
×
253

254
        # Execute the template in a new security context.
255
        security.addContext(self)
1✔
256
        try:
1✔
257
            result = self.pt_render(extra_context=bound_names)
1✔
258
            if keyset is not None:
1!
259
                # Store the result in the cache.
260
                self.ZCacheable_set(result, keywords=keyset)
×
261
            return result
1✔
262
        finally:
263
            security.removeContext(self)
1✔
264

265
        return result
×
266

267
    # Copy over more methods
268
    if HAS_ZSERVER:
1!
269
        security.declareProtected(FTPAccess,  # NOQA: flake8: D001
×
270
                                  'manage_FTPget')
271
        manage_FTPget = get_unbound_function(ZopePageTemplate.manage_FTPget)
×
272

273
    security.declareProtected(View, 'get_size')  # NOQA: flake8: D001
1✔
274
    get_size = get_unbound_function(ZopePageTemplate.get_size)
1✔
275
    getSize = get_size
1✔
276

277
    security.declareProtected(ViewManagementScreens,  # NOQA: flake8: D001
1✔
278
                              'PrincipiaSearchSource')
279
    PrincipiaSearchSource = get_unbound_function(
1✔
280
                                ZopePageTemplate.PrincipiaSearchSource)
281

282
    security.declareProtected(ViewManagementScreens,  # NOQA: flake8: D001
1✔
283
                              'document_src')
284
    document_src = get_unbound_function(ZopePageTemplate.document_src)
1✔
285

286
    pt_getContext = get_unbound_function(ZopePageTemplate.pt_getContext)
1✔
287

288
    source_dot_xml = Src()
1✔
289

290

291
setattr(FSPageTemplate, 'source.xml', FSPageTemplate.source_dot_xml)
1✔
292
setattr(FSPageTemplate, 'source.html', FSPageTemplate.source_dot_xml)
1✔
293
InitializeClass(FSPageTemplate)
1✔
294

295
registerFileExtension('pt', FSPageTemplate)
1✔
296
registerFileExtension('zpt', FSPageTemplate)
1✔
297
registerFileExtension('html', FSPageTemplate)
1✔
298
registerFileExtension('htm', FSPageTemplate)
1✔
299
registerMetaType('Page Template', FSPageTemplate)
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