Back to index

plone3  3.1.7
BaseUnit.py
Go to the documentation of this file.
00001 import os.path
00002 from types import StringType
00003 from zope.interface import implements
00004 
00005 from Products.Archetypes.interfaces import IBaseUnit
00006 from Products.Archetypes.interfaces.base import IBaseUnit as z2IBaseUnit
00007 from Products.Archetypes.config import *
00008 from Products.Archetypes.utils import shasattr
00009 from Products.Archetypes.debug import log
00010 from logging import ERROR
00011 
00012 from AccessControl import ClassSecurityInfo
00013 from Acquisition import aq_base
00014 from Globals import InitializeClass
00015 from OFS.Image import File
00016 from Products.CMFCore import permissions
00017 from Products.CMFCore.utils import getToolByName
00018 from Products.MimetypesRegistry.interfaces import IMimetype
00019 from Products.PortalTransforms.interfaces import idatastream
00020 from webdav.WriteLockInterface import WriteLockInterface
00021 
00022 _marker = []
00023 
00024 class BaseUnit(File):
00025     __implements__ = WriteLockInterface, z2IBaseUnit
00026     implements(IBaseUnit)
00027 
00028     isUnit = 1
00029 
00030     security = ClassSecurityInfo()
00031 
00032     def __init__(self, name, file='', instance=None, **kw):
00033         self.id = self.__name__ = name
00034         self.update(file, instance, **kw)
00035 
00036     def __setstate__(self, dict):
00037         mimetype = dict.get('mimetype', None)
00038         if IMimetype.isImplementedBy(mimetype):
00039             dict['mimetype'] = str(mimetype)
00040             dict['binary'] = not not mimetype.binary
00041         assert(dict.has_key('mimetype'), 'no mimetype in setstate dict')
00042         File.__setstate__(self, dict)
00043 
00044     def update(self, data, instance, **kw):
00045         #Convert from str to unicode as needed
00046         mimetype = kw.get('mimetype', None)
00047         filename = kw.get('filename', None)
00048         encoding = kw.get('encoding', None)
00049         context  = kw.get('context', instance)
00050 
00051         adapter = getToolByName(context, 'mimetypes_registry')
00052         data, filename, mimetype = adapter(data, **kw)
00053 
00054         assert mimetype
00055         self.mimetype = str(mimetype)
00056         self.binary = mimetype.binary
00057         if not self.isBinary():
00058             assert type(data) is type(u'')
00059             if encoding is None:
00060                 try:
00061                     encoding = adapter.guess_encoding(data)
00062                 except UnboundLocalError:
00063                     # adapter is not defined, we are in object creation
00064                     import site
00065                     encoding = site.encoding
00066             self.original_encoding = encoding
00067         else:
00068             self.original_encoding = None
00069         self.raw  = data
00070         self.size = len(data)
00071         # taking care of stupid IE
00072         self.setFilename(filename)
00073         self._cacheExpire()
00074 
00075     def transform(self, instance, mt, **kwargs):
00076         """Takes a mimetype so object.foo.transform('text/plain') should return
00077         a plain text version of the raw content
00078 
00079         return None if no data or if data is untranformable to desired output
00080         mime type
00081         """
00082         encoding = self.original_encoding
00083         orig = self.getRaw(encoding, instance)
00084         if not orig:
00085             return None
00086 
00087         #on ZODB Transaction commit there is by specification
00088         #no acquisition context. If it is not present, take
00089         #the untransformed getRaw, this is necessary for
00090         #being used with APE
00091         # Also don't break if transform was applied with a stale instance
00092         # from the catalog while rebuilding the catalog
00093         if not hasattr(instance, 'aq_parent'):
00094             return orig
00095 
00096         transformer = getToolByName(instance, 'portal_transforms')
00097         data = transformer.convertTo(mt, orig, object=self, usedby=self.id,
00098                                      context=instance,
00099                                      mimetype=self.mimetype,
00100                                      filename=self.filename)
00101 
00102         if data:
00103             assert idatastream.isImplementedBy(data)
00104             _data = data.getData()
00105             instance.addSubObjects(data.getSubObjects())
00106             portal_encoding = kwargs.get('encoding',None) or \
00107                              self.portalEncoding(instance)
00108             encoding = data.getMetadata().get("encoding") or encoding \
00109                        or portal_encoding
00110             if portal_encoding != encoding:
00111                 _data = unicode(_data, encoding).encode(portal_encoding)
00112             return _data
00113 
00114         # we have not been able to transform data
00115         # return the raw data if it's not binary data
00116         # FIXME: is this really the behaviour we want ?
00117         if not self.isBinary():
00118             portal_encoding = kwargs.get('encoding',None) or \
00119                              self.portalEncoding(instance)
00120             if portal_encoding != encoding:
00121                 orig = self.getRaw(portal_encoding)
00122             return orig
00123 
00124         return None
00125 
00126     def __str__(self):
00127         return self.getRaw()
00128 
00129     __call__ = __str__
00130 
00131     def __len__(self):
00132         return self.get_size()
00133 
00134     def isBinary(self):
00135         """Return true if this contains a binary value, else false.
00136         """
00137         try:
00138             return self.binary
00139         except AttributeError:
00140             # XXX workaround for "[ 1040514 ] AttributeError on some types after
00141             # migration 1.2.4rc5->1.3.0"
00142             # Somehow and sometimes the binary attribute gets lost magically
00143             self.binary = not str(self.mimetype).startswith('text')
00144             log("BaseUnit: Failed to access attribute binary for mimetype %s. "
00145                 "Setting binary to %s" % (self.mimetype, self.binary),
00146                 level=ERROR)
00147             return self.binary
00148 
00149     # File handling
00150     def get_size(self):
00151         """Return the file size.
00152         """
00153         return self.size
00154 
00155     def getRaw(self, encoding=None, instance=None):
00156         """Return the file encoded raw value.
00157         """
00158         # fix AT 1.0 backward problems
00159         if not hasattr(aq_base(self),'raw'):
00160             self.raw = self.data
00161             self.size = len(self.raw)
00162 
00163         if self.isBinary():
00164             return self.raw
00165         # FIXME: backward compat, non binary data
00166         # should always be stored as unicode
00167         if not type(self.raw) is type(u''):
00168             return self.raw
00169         if encoding is None:
00170             if instance is None:
00171                 encoding ='UTF-8'
00172             else:
00173                 # FIXME: fallback to portal encoding or original encoding ?
00174                 encoding = self.portalEncoding(instance)
00175         return self.raw.encode(encoding)
00176 
00177     def portalEncoding(self, instance):
00178         """Return the default portal encoding, using an external python script.
00179 
00180         Look the archetypes skin directory for the default implementation.
00181         """
00182         try:
00183             return instance.getCharset()
00184         except AttributeError:
00185             # that occurs during object initialization
00186             # (no acquisition wrapper)
00187             return 'UTF8'
00188 
00189     def getContentType(self):
00190         """Return the file mimetype string.
00191         """
00192         return self.mimetype
00193 
00194     # Backward compatibility
00195     content_type = getContentType
00196 
00197     def setContentType(self, instance, value):
00198         """Set the file mimetype string.
00199         """
00200         mtr = getToolByName(instance, 'mimetypes_registry')
00201         result = mtr.lookup(value)
00202         if not result:
00203             raise ValueError('Unknown mime type %s' % value)
00204         mimetype = result[0]
00205         self.mimetype = str(mimetype)
00206         self.binary = mimetype.binary
00207         self._cacheExpire()
00208 
00209     def getFilename(self):
00210         """Return the file name.
00211         """
00212         return self.filename
00213 
00214     def setFilename(self, filename):
00215         """Set the file name.
00216         """
00217         if type(filename) is StringType:
00218             filename = os.path.basename(filename)
00219             self.filename = filename.split("\\")[-1]
00220         else:
00221             self.filename = filename
00222         self._cacheExpire()
00223 
00224     def _cacheExpire(self):
00225         if shasattr(self, '_v_transform_cache'):
00226             delattr(self, '_v_transform_cache')
00227 
00228     ### index_html
00229     security.declareProtected(permissions.View, "index_html")
00230     def index_html(self, REQUEST, RESPONSE):
00231         """download method"""
00232         filename = self.getFilename()
00233         if filename:
00234             RESPONSE.setHeader('Content-Disposition',
00235                                'attachment; filename=%s' % filename)
00236         RESPONSE.setHeader('Content-Type', self.getContentType())
00237         RESPONSE.setHeader('Content-Length', self.get_size())
00238 
00239         RESPONSE.write(self.getRaw(encoding=self.original_encoding))
00240         return ''
00241 
00242     ### webDAV me this, webDAV me that
00243     security.declareProtected( permissions.ModifyPortalContent, 'PUT')
00244     def PUT(self, REQUEST, RESPONSE):
00245         """Handle HTTP PUT requests"""
00246         self.dav__init(REQUEST, RESPONSE)
00247         self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
00248         mimetype=REQUEST.get_header('Content-Type', None)
00249 
00250         file = REQUEST.get('BODYFILE', _marker)
00251         if file is _marker:
00252             data = REQUEST.get('BODY', _marker)
00253             if data is _marker:
00254                 raise AttributeError, 'REQUEST neither has a BODY nor a BODYFILE'
00255         else:
00256             data = file.read()
00257             file.seek(0)
00258 
00259         self.update(data, self.aq_parent, mimetype=mimetype)
00260 
00261         self.aq_parent.reindexObject()
00262         RESPONSE.setStatus(204)
00263         return RESPONSE
00264 
00265     def manage_FTPget(self, REQUEST, RESPONSE):
00266         """Get the raw content for this unit.
00267 
00268         Also used for the WebDAV SRC.
00269         """
00270         RESPONSE.setHeader('Content-Type', self.getContentType())
00271         RESPONSE.setHeader('Content-Length', self.get_size())
00272         return self.getRaw(encoding=self.original_encoding)
00273 
00274 InitializeClass(BaseUnit)