1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # Copyright (C) 2005-2007 Benoît PIN <benoit.pin@ensmp.fr> #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
20 """ This module implements a portal-managed File class that's inherits of CMFDefault
21 File. If exists, portal_transforms is called to extract text content, and publish
28 from Globals
import InitializeClass
29 from AccessControl
import ClassSecurityInfo
31 from zope
.component
.factory
import Factory
33 from Products
.CMFDefault
.File
import File
as CMFFile
34 from Products
.Photo
.blobbases
import File
as BlobFile
35 from Products
.CMFDefault
.DublinCore
import DefaultDublinCoreImpl
36 from Products
.CMFCore
.permissions
import View
, ModifyPortalContent
37 from Products
.CMFCore
.utils
import getToolByName
38 from hexagonit
.swfheader
import parse
as parseswf
40 class File(BlobFile
, CMFFile
) :
41 #class File(CMFFile) :
42 """ file class with portal_transforms support """
44 security
= ClassSecurityInfo()
46 _properties
= CMFFile
._properties
+ ({'id':'orig_name', 'type':'string', 'mode':'w', 'label':"Original Name"},)
59 , expiration_date
=None
64 BlobFile
.__init
__(self
, id, title
, file, content_type
=content_type
, precondition
=precondition
)
66 #delattr(self, '__name__')
68 # If no file format has been passed in, rely on what OFS.Image.File
69 # detected. Unlike Images, which have code to try and pick the content
70 # type out of the binary data, File objects only provide the correct
71 # type if a "hint" in the form of a filename extension is given.
73 format
= self
.content_type
75 DefaultDublinCoreImpl
.__init
__( self
, title
, subject
, description
76 , contributors
, effective_date
, expiration_date
77 , format
, language
, rights
)
80 def __getattr__(self
, name
) :
81 try : return CMFFile
.__getattr
__(self
, name
)
83 selfAttrs
= self
.__dict
__
84 if selfAttrs
.has_key('_v_transform_cache') :
85 cache
= selfAttrs
['_v_transform_cache']
86 cacheTuple
= cache
.get('text_html', None) # (time, value)
88 cacheData
= cacheTuple
[1]
90 subObDict
= cacheData
.getSubObjects()
91 if subObDict
.has_key(name
) :
92 fileOb
= OFS
.Image
.File(name
, name
, subObDict
[name
])
95 raise AttributeError, name
97 def manage_upload(self
,file='',REQUEST
=None):
98 ret
= super(File
, self
).manage_upload(file=file, REQUEST
=REQUEST
)
100 orig_name
= OFS
.Image
.cookId('', '', file)[0]
102 self
.orig_name
= orig_name
104 if self
.Format() == 'application/x-shockwave-flash' :
107 swfmetadata
= parseswf(file)
109 swfmetadata
= {'width':600, 'height':600}
111 for name
in ('width', 'height') :
112 value
= swfmetadata
[name
]
113 if self
.hasProperty(name
) :
114 self
._updateProperty
(name
, value
)
116 self
.manage_addProperty(name
, value
, 'int')
122 security
.declareProtected(ModifyPortalContent
, 'edit')
123 def edit(self
, precondition
='', file=''):
124 orig_name
= OFS
.Image
.cookId('', '', file)[0]
126 self
.orig_name
= orig_name
127 CMFFile
.edit(self
, precondition
=precondition
, file=file)
128 if hasattr(self
, '_v_transform_cache') :
129 del self
._v
_transform
_cache
132 security
.declareProtected(View
, 'SearchableText')
133 def SearchableText(self
) :
134 """ Return full text"""
135 baseSearchableText
= CMFFile
.SearchableText(self
)
136 transformTool
= getToolByName(self
, 'portal_transforms', default
=None)
137 if transformTool
is None :
138 return baseSearchableText
140 f
= self
.bdata
.open()
142 datastream_text
= transformTool
.convertTo('text/plain',
144 mimetype
= self
.content_type
148 if datastream_text
is not None :
149 full_text
= datastream_text
.getData()
151 return baseSearchableText
+ full_text
153 security
.declareProtected(View
, 'preview')
155 """Return HTML preview if it's possible or empty string """
156 transformTool
= getToolByName(self
, 'portal_transforms', default
= None)
157 if transformTool
is None :
160 filename
= self
.getId().replace(' ', '_')
161 f
= self
.bdata
.open()
163 datastream
= transformTool
.convertTo('text/html',
166 mimetype
= self
.content_type
,
169 if datastream
is not None : return datastream
.getData()
172 security
.declareProtected(View
, 'download')
173 def download(self
, REQUEST
, RESPONSE
):
174 """Download this item.
176 Calls OFS.Image.File.index_html to perform the actual transfer after
177 first setting Content-Disposition to suggest a filename.
179 This method is deprecated, use the URL of this object itself. Because
180 the default view of a File object is to download, rather than view,
181 this method is obsolete. Also note that certain browsers do not deal
182 well with a Content-Disposition header.
186 RESPONSE
.setHeader('Content-Disposition',
187 'attachment; filename=%s' % (self
.orig_name
or self
.getId()))
188 return OFS
.Image
.File
.index_html(self
, REQUEST
, RESPONSE
)
190 security
.declarePublic('getIcon')
191 def getIcon(self
, relative_to_portal
=0):
192 """ return icon corresponding to mime-type
194 regTool
= getToolByName(self
, 'mimetypes_registry', default
=None)
196 f
= self
.bdata
.open()
197 mime
= regTool(f
, mimetype
=self
.content_type
)[2]
199 return mime
.icon_path
201 return CMFFile
.getIcon(self
, relative_to_portal
=relative_to_portal
)
204 InitializeClass(File
)
205 FileFactory
= Factory(File
)
208 def addFile( dispatcher
217 , effective_date
=None
218 , expiration_date
=None
227 # cookId sets the id and title if they are not explicity specified
228 id, title
= OFS
.Image
.cookId(id, title
, file)
230 container
= dispatcher
.Destination()
232 # Instantiate the object and set its description.
233 fobj
= File( id, title
=title
, file='', content_type
=content_type
,
234 precondition
=precondition
, subject
=subject
, description
=description
,
235 contributors
=contributors
, effective_date
=effective_date
,
236 expiration_date
=expiration_date
, format
=format
,
237 language
=language
, rights
=rights
240 # Add the File instance to self
241 container
._setObject
(id, fobj
)
243 # 'Upload' the file. This is done now rather than in the
244 # constructor because the object is now in the ZODB and
245 # can span ZODB objects.
246 container
._getOb
(id).manage_upload(file)