• 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

89.17
/src/Products/CMFCore/FSFile.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 image objects that come from the filesystem.
1✔
14
"""
15

16
import codecs
1✔
17
import os
1✔
18
from warnings import warn
1✔
19

20
import six
1✔
21

22
from AccessControl.class_init import InitializeClass
1✔
23
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
24
from App.special_dtml import DTMLFile
1✔
25
from OFS.Image import File
1✔
26
from zope.contenttype import guess_content_type
1✔
27
from ZPublisher.HTTPRequest import default_encoding
1✔
28

29
from .DirectoryView import registerFileExtension
1✔
30
from .DirectoryView import registerMetaType
1✔
31
from .FSObject import FSObject
1✔
32
from .permissions import FTPAccess
1✔
33
from .permissions import View
1✔
34
from .permissions import ViewManagementScreens
1✔
35
from .utils import _checkConditionalGET
1✔
36
from .utils import _dtmldir
1✔
37
from .utils import _FSCacheHeaders
1✔
38
from .utils import _setCacheHeaders
1✔
39
from .utils import _ViewEmulator
1✔
40

41

42
class FSFile(FSObject):
1✔
43

44
    """FSFiles act like images but are not directly
45
    modifiable from the management interface."""
46
    # Note that OFS.Image.File is not a base class because it is mutable.
47

48
    meta_type = 'Filesystem File'
1✔
49
    zmi_icon = 'far fa-file-archive'
1✔
50
    content_type = 'unknown/unknown'
1✔
51

52
    manage_options = ({'label': 'Customize', 'action': 'manage_main'},)
1✔
53

54
    security = ClassSecurityInfo()
1✔
55
    security.declareObjectProtected(View)
1✔
56

57
    security.declareProtected(ViewManagementScreens, 'manage_main')
1✔
58
    manage_main = DTMLFile('custfile', _dtmldir)
1✔
59

60
    def __init__(self, id, filepath, fullname=None, properties=None):
1✔
61
        id = fullname or id  # Use the whole filename.
1✔
62
        FSObject.__init__(self, id, filepath, fullname, properties)
1✔
63

64
    def _createZODBClone(self):
1✔
65
        return File(self.getId(), '', self._readFile(1))
×
66

67
    def _get_content_type(self, file, body, id, content_type=None):
1✔
68
        # Consult self.content_type first, this is either
69
        # the default (unknown/unknown) or it got a value from a
70
        # .metadata file
71
        default_type = 'unknown/unknown'
1✔
72
        if getattr(self, 'content_type', default_type) != default_type:
1✔
73
            return self.content_type
1✔
74

75
        # Next, look at file headers
76
        headers = getattr(file, 'headers', None)
1✔
77
        if headers and 'content-type' in headers:
1!
78
            content_type = headers['content-type']
×
79
        else:
80
            # Last resort: Use the (imperfect) content type guessing
81
            # mechanism from OFS.Image, which ultimately uses the
82
            # Python mimetypes module.
83
            if not isinstance(body, (six.string_types, six.binary_type)):
1!
84
                body = body.data
×
85
            content_type, enc = guess_content_type(
1✔
86
                getattr(file, 'filename', id), body, content_type)
87
            if (enc is None
1✔
88
                and (content_type.startswith('text/') or
89
                     content_type.startswith('application/'))
90
                    and body.startswith(codecs.BOM_UTF8)):
91
                content_type += '; charset=utf-8'
1✔
92

93
        return content_type
1✔
94

95
    def _readFile(self, reparse):
1✔
96
        """Read the data from the filesystem.
97
        """
98
        file = open(self._filepath, 'rb')
1✔
99
        try:
1✔
100
            data = file.read()
1✔
101
        finally:
102
            file.close()
1✔
103

104
        if reparse or self.content_type == 'unknown/unknown':
1✔
105
            try:
1✔
106
                mtime = os.stat(self._filepath).st_mtime
1✔
107
            except Exception:
×
108
                mtime = 0.0
×
109
            if mtime != self._file_mod_time or mtime == 0.0:
1✔
110
                self.ZCacheable_invalidate()
1✔
111
                self._file_mod_time = mtime
1✔
112
            self.content_type = self._get_content_type(file, data, self.id)
1✔
113
        return data
1✔
114

115
    # The following is mainly taken from OFS/File.py
116

117
    def __str__(self):
1✔
118
        self._updateFromFS()
1✔
119
        if six.PY2:
1!
120
            return str(self._readFile(0))
×
121

122
        data = self._readFile(0)
1✔
123
        ct = self.content_type
1✔
124
        encoding = None
1✔
125

126
        if 'charset=' in ct:
1!
127
            encoding = ct[ct.find('charset=')+8:]
×
128
        elif getattr(self, 'encoding', None):
1✔
129
            encoding = self.encoding
1✔
130
        elif ct.startswith('text/'):
1✔
131
            encoding = default_encoding
1✔
132

133
        if encoding:
1✔
134
            return str(data, encoding=encoding)
1✔
135

136
        warn('Calling str() on non-text data is deprecated, use bytes()',
1✔
137
             DeprecationWarning, stacklevel=2)
138
        return str(data, encoding=default_encoding)
1✔
139

140
    def __bytes__(self):
1✔
141
        self._updateFromFS()
1✔
142
        return bytes(self._readFile(0))
1✔
143

144
    def modified(self):
1✔
145
        return self.getModTime()
1✔
146

147
    @security.protected(View)
1✔
148
    def index_html(self, REQUEST, RESPONSE):
1✔
149
        """
150
        The default view of the contents of a File or Image.
151

152
        Returns the contents of the file or image.  Also, sets the
153
        Content-Type HTTP header to the objects content type.
154
        """
155
        self._updateFromFS()
1✔
156
        view = _ViewEmulator().__of__(self)
1✔
157

158
        # There are 2 Cache Managers which can be in play....
159
        # need to decide which to use to determine where the cache headers
160
        # are decided on.
161
        if self.ZCacheable_getManager() is not None:
1!
162
            self.ZCacheable_set(None)
×
163
        else:
164
            _setCacheHeaders(view, extra_context={})
1✔
165

166
        # If we have a conditional get, set status 304 and return
167
        # no content
168
        if _checkConditionalGET(view, extra_context={}):
1!
169
            return ''
×
170

171
        RESPONSE.setHeader('Content-Type', self.content_type)
1✔
172

173
        # old-style If-Modified-Since header handling.
174
        if self._setOldCacheHeaders():
1✔
175
            # Make sure the CachingPolicyManager gets a go as well
176
            _setCacheHeaders(view, extra_context={})
1✔
177
            return ''
1✔
178

179
        data = self._readFile(0)
1✔
180
        data_len = len(data)
1✔
181
        RESPONSE.setHeader('Content-Length', data_len)
1✔
182
        return data
1✔
183

184
    def _setOldCacheHeaders(self):
1✔
185
        # return False to disable this simple caching behaviour
186
        return _FSCacheHeaders(self)
1✔
187

188
    @security.protected(View)
1✔
189
    def getContentType(self):
1✔
190
        """Get the content type of a file or image.
191

192
        Returns the content type (MIME type) of a file or image.
193
        """
194
        self._updateFromFS()
×
195
        return self.content_type
×
196

197
    security.declareProtected(FTPAccess, 'manage_FTPget')
1✔
198
    manage_FTPget = index_html
1✔
199

200

201
InitializeClass(FSFile)
1✔
202

203
registerFileExtension('doc', FSFile)
1✔
204
registerFileExtension('txt', FSFile)
1✔
205
registerFileExtension('pdf', FSFile)
1✔
206
registerFileExtension('swf', FSFile)
1✔
207
registerFileExtension('jar', FSFile)
1✔
208
registerFileExtension('cab', FSFile)
1✔
209
registerFileExtension('ico', FSFile)
1✔
210
registerFileExtension('js', FSFile)
1✔
211
registerFileExtension('css', FSFile)
1✔
212
registerFileExtension('map', FSFile)
1✔
213
registerFileExtension('svg', FSFile)
1✔
214
registerFileExtension('ttf', FSFile)
1✔
215
registerFileExtension('eot', FSFile)
1✔
216
registerFileExtension('woff', FSFile)
1✔
217
registerFileExtension('woff2', FSFile)
1✔
218
registerMetaType('File', FSFile)
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