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

zopefoundation / Zope / 5286698593

pending completion
5286698593

push

github

icemac
- fix pip version incompatibility under Python 3.12

4283 of 6889 branches covered (62.17%)

Branch coverage included in aggregate %.

27156 of 31453 relevant lines covered (86.34%)

0.86 hits per line

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

75.91
/src/Products/PageTemplates/ZopePageTemplate.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
"""Zope Page Template module (wrapper for the zope.pagetemplate implementation)
1✔
14
"""
15

16
import os
1✔
17

18
from AccessControl.class_init import InitializeClass
1✔
19
from AccessControl.Permissions import change_page_templates
1✔
20
from AccessControl.Permissions import view
1✔
21
from AccessControl.Permissions import view_management_screens
1✔
22
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
23
from AccessControl.SecurityManagement import getSecurityManager
1✔
24
from Acquisition import Acquired
1✔
25
from Acquisition import Explicit
1✔
26
from Acquisition import aq_get
1✔
27
from App.Common import package_home
1✔
28
from OFS.Cache import Cacheable
1✔
29
from OFS.History import Historical
1✔
30
from OFS.History import html_diff
1✔
31
from OFS.PropertyManager import PropertyManager
1✔
32
from OFS.SimpleItem import SimpleItem
1✔
33
from OFS.Traversable import Traversable
1✔
34
from Products.PageTemplates.Expressions import SecureModuleImporter
1✔
35
from Products.PageTemplates.PageTemplate import PageTemplate
1✔
36
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
1✔
37
from Products.PageTemplates.PageTemplateFile import guess_type
1✔
38
from Products.PageTemplates.utils import convertToUnicode
1✔
39
from Shared.DC.Scripts.Script import Script
1✔
40
from Shared.DC.Scripts.Signature import FuncCode
1✔
41
from zExceptions import ResourceLockedError
1✔
42

43

44
preferred_encodings = ['utf-8', 'iso-8859-15']
1✔
45
if 'ZPT_PREFERRED_ENCODING' in os.environ:
1!
46
    preferred_encodings.insert(0, os.environ['ZPT_PREFERRED_ENCODING'])
×
47

48

49
class Src(Explicit):
1✔
50
    """ I am scary code """
51
    security = ClassSecurityInfo()
1✔
52
    security.declareObjectProtected(view_management_screens)
1✔
53

54
    PUT = document_src = Acquired
1✔
55
    index_html = None
1✔
56

57
    def __before_publishing_traverse__(self, ob, request):
1✔
58
        if getattr(request, '_hacked_path', 0):
1✔
59
            request._hacked_path = 0
1✔
60

61
    def __call__(self, REQUEST, RESPONSE):
1✔
62
        " "
63
        return self.document_src(REQUEST)
1✔
64

65

66
InitializeClass(Src)
1✔
67

68

69
class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
1✔
70
                       Traversable, PropertyManager):
71
    "Zope wrapper for Page Template using TAL, TALES, and METAL"
72

73
    meta_type = 'Page Template'
1✔
74
    zmi_icon = 'far fa-file-code'
1✔
75
    output_encoding = 'utf-8'  # provide default for old instances
1✔
76

77
    __code__ = FuncCode((), 0)
1✔
78
    __defaults__ = None
1✔
79

80
    _default_bindings = {'name_subpath': 'traverse_subpath'}
1✔
81
    _default_content_fn = os.path.join(package_home(globals()),
1✔
82
                                       'www', 'default.html')
83

84
    manage_options = (
1✔
85
        {'label': 'Edit', 'action': 'pt_editForm'},
86
        {'label': 'Test', 'action': 'ZScriptHTML_tryForm'},
87
    ) + PropertyManager.manage_options + \
88
        Historical.manage_options + \
89
        SimpleItem.manage_options + \
90
        Cacheable.manage_options
91

92
    _properties = (
1✔
93
        {'id': 'title', 'type': 'string', 'mode': 'w'},
94
        {'id': 'content_type', 'type': 'string', 'mode': 'w'},
95
        {'id': 'output_encoding', 'type': 'string', 'mode': 'w'},
96
        {'id': 'expand', 'type': 'boolean', 'mode': 'w'},
97
    )
98

99
    security = ClassSecurityInfo()
1✔
100
    security.declareObjectProtected(view)
1✔
101

102
    # protect methods from base class(es)
103
    security.declareProtected(view, '__call__')  # NOQA: D001
1✔
104
    security.declareProtected(view_management_screens,  # NOQA: D001
1✔
105
                              'read', 'ZScriptHTML_tryForm')
106

107
    def __init__(self, id, text=None, content_type='text/html', strict=True,
1✔
108
                 output_encoding='utf-8'):
109
        self.id = id
1✔
110
        self.expand = 0
1✔
111
        self.ZBindings_edit(self._default_bindings)
1✔
112
        self.output_encoding = output_encoding
1✔
113
        # default content
114
        if not text:
1✔
115
            with open(self._default_content_fn) as fd:
1✔
116
                text = fd.read()
1✔
117
            content_type = 'text/html'
1✔
118
        self.pt_edit(text, content_type)
1✔
119

120
    @security.protected(change_page_templates)
1✔
121
    def pt_edit(self, text, content_type, keep_output_encoding=False):
1✔
122
        if not isinstance(text, str):
1✔
123
            (text_decoded,
1✔
124
             source_encoding) = convertToUnicode(text, content_type,
125
                                                 preferred_encodings)
126
            output_encoding = source_encoding
1✔
127
        else:
128
            text_decoded = text
1✔
129
            source_encoding = None
1✔
130
            output_encoding = 'utf-8'
1✔
131

132
        # for content updated through WebDAV
133
        if not keep_output_encoding:
1!
134
            self.output_encoding = output_encoding
1✔
135

136
        text_decoded = text_decoded.strip()
1✔
137

138
        self.ZCacheable_invalidate()
1✔
139
        super().pt_edit(text_decoded, content_type)
1✔
140

141
    pt_editForm = PageTemplateFile('www/ptEdit', globals(),
1✔
142
                                   __name__='pt_editForm')
143
    pt_editForm._owner = None
1✔
144
    manage = manage_main = pt_editForm
1✔
145

146
    source_dot_xml = Src()
1✔
147

148
    @security.protected(change_page_templates)
1✔
149
    def pt_editAction(self, REQUEST, title, text, content_type, expand=0):
1✔
150
        """Change the title and document."""
151

152
        if self.wl_isLocked():
×
153
            raise ResourceLockedError("File is locked.")
×
154

155
        self.expand = expand
×
156

157
        # The ZMI edit view uses utf-8! So we can safely assume
158
        # that 'title' and 'text' are utf-8 encoded strings - hopefully
159

160
        self.pt_setTitle(title, 'utf-8')
×
161
        self.pt_edit(text, content_type, True)
×
162
        REQUEST.set('text', self.read())  # May not equal 'text'!
×
163
        REQUEST.set('title', self.title)
×
164
        message = "Saved changes."
×
165
        if getattr(self, '_v_warnings', None):
×
166
            message = ("<strong>Warning:</strong> <i>%s</i>"
×
167
                       % '<br>'.join(self._v_warnings))
168
        return self.pt_editForm(manage_tabs_message=message)
×
169

170
    @security.protected(change_page_templates)
1✔
171
    def pt_setTitle(self, title, encoding='utf-8'):
1✔
172
        if not isinstance(title, str):
1✔
173
            title = title.decode(encoding)
1✔
174
        self._setPropValue('title', title)
1✔
175

176
    def _setPropValue(self, id, value):
1✔
177
        """ set a property and invalidate the cache """
178
        PropertyManager._setPropValue(self, id, value)
1✔
179
        self.ZCacheable_invalidate()
1✔
180

181
    @security.protected(change_page_templates)
1✔
182
    def pt_upload(self, REQUEST, file='', encoding='utf-8'):
1✔
183
        """Replace the document with the text in file."""
184

185
        if self.wl_isLocked():
1!
186
            raise ResourceLockedError("File is locked.")
×
187

188
        if not file:
1!
189
            return self.pt_editForm(manage_tabs_message='No file specified',
1✔
190
                                    manage_tabs_type='warning')
191

192
        if hasattr(file, 'read'):
×
193
            text = file.read()
×
194
            filename = file.filename
×
195
        else:
196
            filename = None
×
197
            text = file
×
198

199
        if isinstance(text, bytes):
×
200
            content_type = guess_type(filename, text)
×
201
            (text,
×
202
             source_encoding) = convertToUnicode(text, content_type,
203
                                                 preferred_encodings)
204
        elif isinstance(text, str):
×
205
            content_type = guess_type(filename, text.encode('utf-8'))
×
206

207
        self.pt_edit(text, content_type)
×
208
        return self.pt_editForm(manage_tabs_message='Saved changes')
×
209

210
    def ZScriptHTML_tryParams(self):
1✔
211
        """Parameters to test the script with."""
212
        return []
×
213

214
    def manage_historyCompare(self, rev1, rev2, REQUEST,
1✔
215
                              historyComparisonResults=''):
216
        return ZopePageTemplate.inheritedAttribute(
×
217
            'manage_historyCompare')(
218
            self, rev1, rev2, REQUEST,
219
            historyComparisonResults=html_diff(rev1._text, rev2._text))
220

221
    def pt_getContext(self, *args, **kw):
1✔
222
        root = None
1✔
223
        meth = aq_get(self, 'getPhysicalRoot', None)
1✔
224
        if meth is not None:
1✔
225
            root = meth()
1✔
226
        context = self._getContext()
1✔
227
        c = {'template': self,
1✔
228
             'here': context,
229
             'context': context,
230
             'container': self._getContainer(),
231
             'nothing': None,
232
             'options': {},
233
             'root': root,
234
             'request': aq_get(root, 'REQUEST', None),
235
             'modules': SecureModuleImporter,
236
             }
237
        return c
1✔
238

239
    def write(self, text):
1✔
240
        if not isinstance(text, str):
1!
241
            text, encoding = convertToUnicode(text,
×
242
                                              self.content_type,
243
                                              preferred_encodings)
244
            self.output_encoding = encoding
×
245

246
        self.ZCacheable_invalidate()
1✔
247
        ZopePageTemplate.inheritedAttribute('write')(self, text)
1✔
248

249
    def _exec(self, bound_names, args, kw):
1✔
250
        """Call a Page Template"""
251
        if 'args' not in kw:
1!
252
            kw['args'] = args
1✔
253
        bound_names['options'] = kw
1✔
254

255
        request = aq_get(self, 'REQUEST', None)
1✔
256
        if request is not None:
1✔
257
            response = request.response
1✔
258
            if 'content-type' not in response.headers:
1✔
259
                response.setHeader('content-type', self.content_type)
1✔
260

261
        security = getSecurityManager()
1✔
262
        bound_names['user'] = security.getUser()
1✔
263

264
        # Retrieve the value from the cache.
265
        keyset = None
1✔
266
        if self.ZCacheable_isCachingEnabled():
1!
267
            # Prepare a cache key.
268
            keyset = {'here': self._getContext(),
×
269
                      'bound_names': bound_names}
270
            result = self.ZCacheable_get(keywords=keyset)
×
271
            if result is not None:
×
272
                # Got a cached value.
273
                return result
×
274

275
        # Execute the template in a new security context.
276
        security.addContext(self)
1✔
277

278
        try:
1✔
279
            result = self.pt_render(extra_context=bound_names)
1✔
280
            if keyset is not None:
1!
281
                # Store the result in the cache.
282
                self.ZCacheable_set(result, keywords=keyset)
×
283
            return result
1✔
284
        finally:
285
            security.removeContext(self)
1✔
286

287
    security.declareProtected(  # NOQA: D001
1✔
288
        change_page_templates,
289
        'manage_historyCopy',
290
        'manage_beforeHistoryCopy',
291
        'manage_afterHistoryCopy')
292

293
    @security.protected(view_management_screens)
1✔
294
    def html(self):
1✔
295
        return self.content_type == 'text/html'
1✔
296

297
    @security.protected(view_management_screens)
1✔
298
    def get_size(self):
1✔
299
        return len(self.read())
1✔
300

301
    security.declareProtected(view_management_screens, 'getSize')  # NOQA: D001
1✔
302
    getSize = get_size
1✔
303

304
    @security.protected(view_management_screens)
1✔
305
    def PrincipiaSearchSource(self):
1✔
306
        "Support for searching - the document's contents are searched."
307
        return self.read()
×
308

309
    @security.protected(view_management_screens)
1✔
310
    def document_src(self, REQUEST=None, RESPONSE=None):
1✔
311
        """Return expanded document source."""
312
        if RESPONSE is not None:
1!
313
            RESPONSE.setHeader('Content-Type', 'text/plain')
×
314
        if REQUEST is not None and REQUEST.get('raw'):
1!
315
            return self._text
×
316
        return self.read()
1✔
317

318
    @security.protected(view)
1✔
319
    def pt_source_file(self):
1✔
320
        """Returns a file name to be compiled into the TAL code."""
321
        try:
1✔
322
            return '/'.join(self.getPhysicalPath())
1✔
323
        except Exception:
1✔
324
            # This page template is being compiled without an
325
            # acquisition context, so we don't know where it is. :-(
326
            return None
1✔
327

328
    def __setstate__(self, state):
1✔
329
        # Perform on-the-fly migration to text_type.
330
        # Perhaps it might be better to work with the 'generation' module
331
        # here?
332
        _text = state.get('_text')
1✔
333
        if _text is not None and not isinstance(state['_text'], str):
1!
334
            text, encoding = convertToUnicode(
×
335
                state['_text'],
336
                state.get('content_type', 'text/html'),
337
                preferred_encodings)
338
            state['_text'] = text
×
339
            state['output_encoding'] = encoding
×
340
        self.__dict__.update(state)
1✔
341

342
    def pt_render(self, source=False, extra_context={}):
1✔
343
        result = PageTemplate.pt_render(self, source, extra_context)
1✔
344
        assert isinstance(result, str)
1✔
345
        return result
1✔
346

347
    @security.protected(change_page_templates)
1✔
348
    def PUT(self, REQUEST, RESPONSE):
1✔
349
        """ Handle HTTP PUT requests """
350

351
        self.dav__init(REQUEST, RESPONSE)
1✔
352
        self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
1✔
353
        text = REQUEST.get('BODY', '')
1✔
354
        content_type = guess_type(self.getId(), text)
1✔
355
        self.pt_edit(text, content_type)
1✔
356
        RESPONSE.setStatus(204)
1✔
357
        return RESPONSE
1✔
358

359

360
InitializeClass(ZopePageTemplate)
1✔
361

362
setattr(ZopePageTemplate, 'source.xml', ZopePageTemplate.source_dot_xml)
1✔
363
setattr(ZopePageTemplate, 'source.html', ZopePageTemplate.source_dot_xml)
1✔
364

365
# Product registration and Add support
366
manage_addPageTemplateForm = PageTemplateFile(
1✔
367
    'www/ptAdd', globals(), __name__='manage_addPageTemplateForm')
368

369

370
def manage_addPageTemplate(self, id, title='', text='', encoding='utf-8',
1✔
371
                           submit=None, REQUEST=None, RESPONSE=None):
372
    "Add a Page Template with optional file content."
373

374
    filename = ''
1✔
375
    filehandle = None
1✔
376
    content_type = 'text/html'
1✔
377

378
    if REQUEST and hasattr(REQUEST.get('file', None), 'read'):
1✔
379
        filehandle = REQUEST['file']
1✔
380
    elif hasattr(text, 'read'):
1!
381
        filehandle = text
×
382

383
    if filehandle is not None:
1✔
384
        filename = getattr(filehandle, 'filename', '')
1✔
385
        text = filehandle.read()
1✔
386
        headers = getattr(filehandle, 'headers', None)
1✔
387
        if headers and 'content_type' in headers:
1!
388
            content_type = headers['content_type']
1✔
389
        else:
390
            content_type = guess_type(filename, text)
×
391

392
    # ensure that we pass text_type to the constructor to
393
    # avoid further hassles with pt_edit()
394

395
    if not isinstance(text, str):
1✔
396
        text = text.decode(encoding)
1✔
397

398
    zpt = ZopePageTemplate(id, text, content_type, output_encoding=encoding)
1✔
399
    zpt.pt_setTitle(title, encoding)
1✔
400
    self._setObject(id, zpt)
1✔
401
    zpt = getattr(self, id)
1✔
402

403
    if RESPONSE:
1!
404
        if submit == "Add and Edit":
×
405
            RESPONSE.redirect(zpt.absolute_url() + '/pt_editForm')
×
406
        else:
407
            RESPONSE.redirect(self.absolute_url() + '/manage_main')
×
408
    else:
409
        return zpt
1✔
410

411

412
def initialize(context):
1✔
413
    context.registerClass(
1✔
414
        ZopePageTemplate,
415
        permission='Add Page Templates',
416
        constructors=(manage_addPageTemplateForm,
417
                      manage_addPageTemplate),
418
    )
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