Back to index

plone3  3.1.7
FSObject.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """ Customizable objects that come from the filesystem (base class).
00014 
00015 $Id: FSObject.py 76996 2007-06-24 00:18:49Z hannosch $
00016 """
00017 
00018 from os import path, stat
00019 
00020 import Globals
00021 from AccessControl import ClassSecurityInfo
00022 from AccessControl.Permission import Permission
00023 from AccessControl.Role import RoleManager
00024 from Acquisition import aq_base
00025 from Acquisition import aq_inner
00026 from Acquisition import aq_parent
00027 from Acquisition import Implicit
00028 from DateTime import DateTime
00029 from OFS.Cache import Cacheable
00030 from OFS.SimpleItem import Item
00031 from Products.PythonScripts.standard import html_quote
00032 
00033 from permissions import ManagePortal
00034 from permissions import View
00035 from permissions import ViewManagementScreens
00036 from utils import getToolByName
00037 
00038 
00039 class FSObject(Implicit, Item, RoleManager, Cacheable):
00040 
00041     """FSObject is a base class for all filesystem based look-alikes.
00042 
00043     Subclasses of this class mimic ZODB based objects like Image and
00044     DTMLMethod, but are not directly modifiable from the management
00045     interface. They provide means to create a TTW editable copy, however.
00046     """
00047 
00048     # Always empty for FS based, non-editable objects.
00049     title = ''
00050     _file_mod_time = 0
00051     _parsed = 0
00052 
00053     security = ClassSecurityInfo()
00054     security.declareObjectProtected(View)
00055 
00056     def __init__(self, id, filepath, fullname=None, properties=None):
00057         if properties:
00058             # Since props come from the filesystem, this should be
00059             # safe.
00060             self.__dict__.update(properties)
00061             if fullname and properties.get('keep_extension', 0):
00062                 id = fullname
00063 
00064             cache = properties.get('cache')
00065             if cache:
00066                 self.ZCacheable_setManagerId(cache)
00067 
00068         self.id = id
00069         self.__name__ = id # __name__ is used in traceback reporting
00070         self._filepath = filepath
00071 
00072         try:
00073              self._file_mod_time = stat(filepath)[8]
00074         except:
00075              pass
00076         self._readFile(0)
00077 
00078     security.declareProtected(ViewManagementScreens, 'manage_doCustomize')
00079     def manage_doCustomize(self, folder_path, RESPONSE=None, \
00080                            root=None, obj=None):
00081         """Makes a ZODB Based clone with the same data.
00082 
00083         Calls _createZODBClone for the actual work.
00084         """
00085 
00086         if obj is None:
00087             obj = self._createZODBClone()
00088         parent = aq_parent(aq_inner(self))
00089 
00090         # Preserve cache manager associations
00091         cachemgr_id = self.ZCacheable_getManagerId()
00092         if ( cachemgr_id and
00093              getattr(obj, 'ZCacheable_setManagerId', None) is not None ):
00094             obj.ZCacheable_setManagerId(cachemgr_id)
00095 
00096         # If there are proxy roles we preserve them
00097         proxy_roles = getattr(aq_base(self), '_proxy_roles', None)
00098         if proxy_roles is not None and isinstance(proxy_roles, tuple):
00099             obj._proxy_roles = tuple(self._proxy_roles)
00100 
00101         # Also, preserve any permission settings that might have come
00102         # from a metadata file or from fiddling in the ZMI
00103         old_info = [x[:2] for x in self.ac_inherited_permissions(1)]
00104         for old_perm, value in old_info:
00105             p = Permission(old_perm, value, self)
00106             acquired = int(isinstance(p.getRoles(default=[]), list))
00107             rop_info = self.rolesOfPermission(old_perm)
00108             roles = [x['name'] for x in rop_info if x['selected'] != '']
00109             try:
00110                 # if obj is based on OFS.ObjectManager an acquisition context is
00111                 # required for _subobject_permissions()
00112                 obj.__of__(parent).manage_permission(old_perm, roles=roles,
00113                                                      acquire=acquired)
00114             except ValueError:
00115                 # The permission was invalid, never mind
00116                 pass
00117 
00118         skins_tool_namegetter = getattr(self, 'getSkinsFolderName', None)
00119         if skins_tool_namegetter is not None:
00120             skins_tool_name = skins_tool_namegetter()
00121         else:
00122             skins_tool_name = 'portal_skins'
00123 
00124         id = obj.getId()
00125         fpath = tuple( folder_path.split('/') )
00126         if root is None:
00127             portal_skins = getToolByName(self, skins_tool_name)
00128         else:
00129             portal_skins = root
00130         if folder_path == '.':
00131             fpath = ()
00132         folder = portal_skins.restrictedTraverse(fpath)
00133         if id in folder.objectIds():
00134             # we cant catch the badrequest so
00135             # we'll that to check before hand
00136             obj = folder._getOb(id)
00137             if RESPONSE is not None:
00138                 RESPONSE.redirect('%s/manage_main?manage_tabs_message=%s' % (
00139                     obj.absolute_url(), html_quote("An object with this id already exists")
00140                     ))
00141         else:
00142             folder._verifyObjectPaste(obj, validate_src=0)
00143             folder._setObject(id, obj)
00144 
00145             if RESPONSE is not None:
00146                 RESPONSE.redirect('%s/%s/manage_main' % (
00147                 folder.absolute_url(), id))
00148 
00149         if RESPONSE is not None:
00150             RESPONSE.redirect('%s/%s/manage_main' % (
00151                 folder.absolute_url(), id))
00152 
00153     def _createZODBClone(self):
00154         """Create a ZODB (editable) equivalent of this object."""
00155         raise NotImplementedError, "This should be implemented in a subclass."
00156 
00157     def _readFile(self, reparse):
00158         """Read the data from the filesystem.
00159 
00160         Read the file indicated by self._filepath, and parse the
00161         data if necessary.  'reparse' is set when reading the second
00162         time and beyond.
00163         """
00164         raise NotImplementedError, "This should be implemented in a subclass."
00165 
00166     # Refresh our contents from the filesystem if that is newer and we are
00167     # running in debug mode.
00168     def _updateFromFS(self):
00169         parsed = self._parsed
00170         if not parsed or Globals.DevelopmentMode:
00171             try:
00172                 mtime = stat(self._filepath)[8]
00173             except:
00174                 mtime = 0
00175             if not parsed or mtime != self._file_mod_time:
00176                 # if we have to read the file again, remove the cache
00177                 self.ZCacheable_invalidate()
00178                 self._readFile(1)
00179                 self._file_mod_time = mtime
00180                 self._parsed = 1
00181 
00182     security.declareProtected(View, 'get_size')
00183     def get_size(self):
00184         """Get the size of the underlying file."""
00185         return path.getsize(self._filepath)
00186 
00187     security.declareProtected(View, 'getModTime')
00188     def getModTime(self):
00189         """Return the last_modified date of the file we represent.
00190 
00191         Returns a DateTime instance.
00192         """
00193         self._updateFromFS()
00194         return DateTime(self._file_mod_time)
00195 
00196     security.declareProtected(ViewManagementScreens, 'getObjectFSPath')
00197     def getObjectFSPath(self):
00198         """Return the path of the file we represent"""
00199         self._updateFromFS()
00200         return self._filepath
00201 
00202 Globals.InitializeClass(FSObject)
00203 
00204 
00205 class BadFile(FSObject):
00206 
00207     """
00208         Represent a file which was not readable or parseable
00209         as its intended type.
00210     """
00211     meta_type = 'Bad File'
00212     icon = 'p_/broken'
00213 
00214     BAD_FILE_VIEW = """\
00215 <dtml-var manage_page_header>
00216 <dtml-var manage_tabs>
00217 <h2> Bad Filesystem Object: &dtml-getId; </h2>
00218 
00219 <h3> File Contents </h3>
00220 <pre>
00221 <dtml-var getFileContents>
00222 </pre>
00223 
00224 <h3> Exception </h3>
00225 <pre>
00226 <dtml-var getExceptionText>
00227 </pre>
00228 <dtml-var manage_page_footer>
00229 """
00230 
00231     manage_options = ({'label':'Error', 'action':'manage_showError'},)
00232 
00233     def __init__( self, id, filepath, exc_str=''
00234                 , fullname=None, properties=None):
00235         id = fullname or id # Use the whole filename.
00236         self.exc_str = exc_str
00237         self.file_contents = ''
00238         FSObject.__init__(self, id, filepath, fullname, properties)
00239 
00240     security = ClassSecurityInfo()
00241 
00242     showError = Globals.HTML( BAD_FILE_VIEW )
00243     security.declareProtected(ManagePortal, 'manage_showError')
00244     def manage_showError( self, REQUEST ):
00245         """
00246         """
00247         return self.showError( self, REQUEST )
00248 
00249     security.declarePrivate('_readFile')
00250     def _readFile(self, reparse):
00251         """Read the data from the filesystem.
00252         """
00253         try:
00254             file = open(self._filepath, 'rb')
00255             try:
00256                 data = self.file_contents = file.read()
00257             finally:
00258                 file.close()
00259         except:  # No errors of any sort may propagate
00260             data = self.file_contents = None #give up
00261         return data
00262 
00263     security.declarePublic( 'getFileContents' )
00264     def getFileContents( self ):
00265         """
00266             Return the contents of the file, if we could read it.
00267         """
00268         return self.file_contents
00269 
00270     security.declarePublic( 'getExceptionText' )
00271     def getExceptionText( self ):
00272         """
00273             Return the exception thrown while reading or parsing
00274             the file.
00275         """
00276         return self.exc_str
00277 
00278 Globals.InitializeClass( BadFile )