vendor/CMF/1.6.3/CMFCore

view FSObject.py @ 2:4c712d7bd1d7

Added tag 1.6.3 for changeset 1babb9d61518
author Georges Racinet on purity.racinet.fr <georges@racinet.fr>
date Fri, 09 Sep 2011 12:44:00 +0200
parents
children
line source
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.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 objects that come from the filesystem (base class).
15 $Id$
16 """
18 from os import path, stat
20 import Globals
21 from AccessControl import ClassSecurityInfo
22 from AccessControl.Role import RoleManager
23 from AccessControl.Permission import Permission
24 from Acquisition import Implicit
25 from Acquisition import aq_base
26 from Acquisition import aq_inner
27 from Acquisition import aq_parent
28 from OFS.Cache import Cacheable
29 from OFS.SimpleItem import Item
30 from DateTime import DateTime
31 from Products.PythonScripts.standard import html_quote
33 from permissions import ManagePortal
34 from permissions import View
35 from permissions import ViewManagementScreens
36 from utils import expandpath
37 from utils import getToolByName
40 class FSObject(Implicit, Item, RoleManager, Cacheable):
41 """FSObject is a base class for all filesystem based look-alikes.
43 Subclasses of this class mimic ZODB based objects like Image and
44 DTMLMethod, but are not directly modifiable from the management
45 interface. They provide means to create a TTW editable copy, however.
46 """
48 # Always empty for FS based, non-editable objects.
49 title = ''
51 security = ClassSecurityInfo()
52 security.declareObjectProtected(View)
54 _file_mod_time = 0
55 _parsed = 0
57 def __init__(self, id, filepath, fullname=None, properties=None):
58 if properties:
59 # Since props come from the filesystem, this should be
60 # safe.
61 self.__dict__.update(properties)
62 if fullname and properties.get('keep_extension', 0):
63 id = fullname
65 cache = properties.get('cache')
66 if cache:
67 self.ZCacheable_setManagerId(cache)
69 self.id = id
70 self.__name__ = id # __name__ is used in traceback reporting
71 self._filepath = filepath
72 fp = expandpath(self._filepath)
74 try: self._file_mod_time = stat(fp)[8]
75 except: pass
76 self._readFile(0)
78 security.declareProtected(ViewManagementScreens, 'manage_doCustomize')
79 def manage_doCustomize(self, folder_path, RESPONSE=None):
80 """Makes a ZODB Based clone with the same data.
82 Calls _createZODBClone for the actual work.
83 """
85 obj = self._createZODBClone()
86 parent = aq_parent(aq_inner(self))
88 # Preserve cache manager associations
89 cachemgr_id = self.ZCacheable_getManagerId()
90 if ( cachemgr_id and
91 getattr(obj, 'ZCacheable_setManagerId', None) is not None ):
92 obj.ZCacheable_setManagerId(cachemgr_id)
94 # If there are proxy roles we preserve them
95 proxy_roles = getattr(aq_base(self), '_proxy_roles', None)
96 if proxy_roles is not None and isinstance(proxy_roles, tuple):
97 obj._proxy_roles = tuple(self._proxy_roles)
99 # Also, preserve any permission settings that might have come
100 # from a metadata file or from fiddling in the ZMI
101 old_info = [x[:2] for x in self.ac_inherited_permissions(1)]
102 for old_perm, value in old_info:
103 p = Permission(old_perm, value, self)
104 acquired = int(isinstance(p.getRoles(default=[]), list))
105 rop_info = self.rolesOfPermission(old_perm)
106 roles = [x['name'] for x in rop_info if x['selected'] != '']
107 try:
108 # if obj is based on OFS.ObjectManager an acquisition context is
109 # required for _subobject_permissions()
110 obj.__of__(parent).manage_permission(old_perm, roles=roles,
111 acquire=acquired)
112 except ValueError:
113 # The permission was invalid, never mind
114 pass
116 skins_tool_namegetter = getattr(self, 'getSkinsFolderName', None)
117 if skins_tool_namegetter is not None:
118 skins_tool_name = skins_tool_namegetter()
119 else:
120 skins_tool_name = 'portal_skins'
122 id = obj.getId()
123 fpath = tuple( folder_path.split('/') )
124 portal_skins = getToolByName(self, skins_tool_name)
125 folder = portal_skins.restrictedTraverse(fpath)
126 if id in folder.objectIds():
127 # we cant catch the badrequest so
128 # we'll that to check before hand
129 obj = folder._getOb(id)
130 if RESPONSE is not None:
131 RESPONSE.redirect('%s/manage_main?manage_tabs_message=%s' % (
132 obj.absolute_url(), html_quote("An object with this id already exists")
133 ))
134 else:
135 folder._verifyObjectPaste(obj, validate_src=0)
136 folder._setObject(id, obj)
138 if RESPONSE is not None:
139 RESPONSE.redirect('%s/%s/manage_main' % (
140 folder.absolute_url(), id))
142 if RESPONSE is not None:
143 RESPONSE.redirect('%s/%s/manage_main' % (
144 folder.absolute_url(), id))
146 def _createZODBClone(self):
147 """Create a ZODB (editable) equivalent of this object."""
148 raise NotImplementedError, "This should be implemented in a subclass."
150 def _readFile(self, reparse):
151 """Read the data from the filesystem.
153 Read the file indicated by exandpath(self._filepath), and parse the
154 data if necessary. 'reparse' is set when reading the second
155 time and beyond.
156 """
157 raise NotImplementedError, "This should be implemented in a subclass."
159 # Refresh our contents from the filesystem if that is newer and we are
160 # running in debug mode.
161 def _updateFromFS(self):
162 parsed = self._parsed
163 if not parsed or Globals.DevelopmentMode:
164 fp = expandpath(self._filepath)
165 try: mtime=stat(fp)[8]
166 except: mtime=0
167 if not parsed or mtime != self._file_mod_time:
168 # if we have to read the file again, remove the cache
169 self.ZCacheable_invalidate()
170 self._readFile(1)
171 self._file_mod_time = mtime
172 self._parsed = 1
174 security.declareProtected(View, 'get_size')
175 def get_size(self):
176 """Get the size of the underlying file."""
177 fp = expandpath(self._filepath)
178 return path.getsize(fp)
180 security.declareProtected(View, 'getModTime')
181 def getModTime(self):
182 """Return the last_modified date of the file we represent.
184 Returns a DateTime instance.
185 """
186 self._updateFromFS()
187 return DateTime(self._file_mod_time)
189 security.declareProtected(ViewManagementScreens, 'getObjectFSPath')
190 def getObjectFSPath(self):
191 """Return the path of the file we represent"""
192 self._updateFromFS()
193 return self._filepath
195 Globals.InitializeClass(FSObject)
198 class BadFile( FSObject ):
199 """
200 Represent a file which was not readable or parseable
201 as its intended type.
202 """
203 meta_type = 'Bad File'
204 icon = 'p_/broken'
206 BAD_FILE_VIEW = """\
207 <dtml-var manage_page_header>
208 <dtml-var manage_tabs>
209 <h2> Bad Filesystem Object: &dtml-getId; </h2>
211 <h3> File Contents </h3>
212 <pre>
213 <dtml-var getFileContents>
214 </pre>
216 <h3> Exception </h3>
217 <pre>
218 <dtml-var getExceptionText>
219 </pre>
220 <dtml-var manage_page_footer>
221 """
223 manage_options=(
224 {'label':'Error', 'action':'manage_showError'},
225 )
227 def __init__( self, id, filepath, exc_str=''
228 , fullname=None, properties=None):
229 id = fullname or id # Use the whole filename.
230 self.exc_str = exc_str
231 self.file_contents = ''
232 FSObject.__init__(self, id, filepath, fullname, properties)
234 security = ClassSecurityInfo()
236 showError = Globals.HTML( BAD_FILE_VIEW )
237 security.declareProtected(ManagePortal, 'manage_showError')
238 def manage_showError( self, REQUEST ):
239 """
240 """
241 return self.showError( self, REQUEST )
243 security.declarePrivate( '_readFile' )
244 def _readFile( self, reparse ):
245 """Read the data from the filesystem.
247 Read the file indicated by exandpath(self._filepath), and parse the
248 data if necessary. 'reparse' is set when reading the second
249 time and beyond.
250 """
251 try:
252 fp = expandpath(self._filepath)
253 file = open(fp, 'rb')
254 try:
255 data = self.file_contents = file.read()
256 finally:
257 file.close()
258 except: # No errors of any sort may propagate
259 data = self.file_contents = None #give up
260 return data
262 security.declarePublic( 'getFileContents' )
263 def getFileContents( self ):
264 """
265 Return the contents of the file, if we could read it.
266 """
267 return self.file_contents
269 security.declarePublic( 'getExceptionText' )
270 def getExceptionText( self ):
271 """
272 Return the exception thrown while reading or parsing
273 the file.
274 """
275 return self.exc_str
277 Globals.InitializeClass( BadFile )