22a1101a9444f329d9dbece06e5175714b586d63
[Plinn.git] / File.py
1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # Copyright (C) 2005-2007 BenoƮt PIN <benoit.pin@ensmp.fr> #
5 # #
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. #
10 # #
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. #
15 # #
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
22 attachments.
23
24
25
26 """
27
28 from Globals import InitializeClass
29 from AccessControl import ClassSecurityInfo
30 import OFS
31 from zope.component.factory import Factory
32
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
39
40 class File(BlobFile, CMFFile) :
41 #class File(CMFFile) :
42 """ file class with portal_transforms support """
43
44 security = ClassSecurityInfo()
45
46 _properties = CMFFile._properties + ({'id':'orig_name', 'type':'string', 'mode':'w', 'label':"Original Name"},)
47 orig_name = ''
48
49 def __init__( self
50 , id
51 , title=''
52 , file=''
53 , content_type=''
54 , precondition=''
55 , subject=()
56 , description=''
57 , contributors=()
58 , effective_date=None
59 , expiration_date=None
60 , format=None
61 , language='en-US'
62 , rights=''
63 ):
64 BlobFile.__init__(self, id, title, file, content_type=content_type, precondition=precondition)
65 self._setId(id)
66 #delattr(self, '__name__')
67 #
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.
72 if format is None:
73 format = self.content_type
74
75 DefaultDublinCoreImpl.__init__( self, title, subject, description
76 , contributors, effective_date, expiration_date
77 , format, language, rights )
78
79
80 def __getattr__(self, name) :
81 try : return CMFFile.__getattr__(self, name)
82 except :
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)
87 if cacheTuple :
88 cacheData = cacheTuple[1]
89
90 subObDict = cacheData.getSubObjects()
91 if subObDict.has_key(name) :
92 fileOb = OFS.Image.File(name, name, subObDict[name])
93 return fileOb
94
95 raise AttributeError, name
96
97 def manage_upload(self,file='',REQUEST=None):
98 ret = super(File, self).manage_upload(file=file, REQUEST=REQUEST)
99
100 orig_name = OFS.Image.cookId('', '', file)[0]
101 if orig_name :
102 self.orig_name = orig_name
103
104 if self.Format() == 'application/x-shockwave-flash' :
105 if file :
106 try :
107 swfmetadata = parseswf(file)
108 except IOError :
109 swfmetadata = {'width':600, 'height':600}
110
111 for name in ('width', 'height') :
112 value = swfmetadata[name]
113 if self.hasProperty(name) :
114 self._updateProperty(name, value)
115 else :
116 self.manage_addProperty(name, value, 'int')
117 self.reindexObject()
118 return ret
119
120
121
122 security.declareProtected(ModifyPortalContent, 'edit')
123 def edit(self, precondition='', file=''):
124 orig_name = OFS.Image.cookId('', '', file)[0]
125 if orig_name :
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
130
131
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
139 else :
140 f = self.bdata.open()
141 orig = f.read()
142 datastream_text = transformTool.convertTo('text/plain',
143 orig,
144 mimetype = self.content_type
145 )
146 f.close()
147 full_text = ''
148 if datastream_text is not None :
149 full_text = datastream_text.getData()
150
151 return baseSearchableText + full_text
152
153 security.declareProtected(View, 'preview')
154 def preview(self) :
155 """Return HTML preview if it's possible or empty string """
156 transformTool = getToolByName(self, 'portal_transforms', default = None)
157 if transformTool is None :
158 return ''
159 else :
160 filename = self.getId().replace(' ', '_')
161 f = self.bdata.open()
162 orig = f.read()
163 datastream = transformTool.convertTo('text/html',
164 orig,
165 object=self,
166 mimetype = self.content_type,
167 filename = filename)
168 f.close()
169 if datastream is not None : return datastream.getData()
170 else : return ''
171
172 security.declareProtected(View, 'download')
173 def download(self, REQUEST, RESPONSE):
174 """Download this item.
175
176 Calls OFS.Image.File.index_html to perform the actual transfer after
177 first setting Content-Disposition to suggest a filename.
178
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.
183
184 """
185
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)
189
190 security.declarePublic('getIcon')
191 def getIcon(self, relative_to_portal=0):
192 """ return icon corresponding to mime-type
193 """
194 regTool = getToolByName(self, 'mimetypes_registry', default=None)
195 if regTool :
196 f = self.bdata.open()
197 mime = regTool(f, mimetype=self.content_type)[2]
198 f.close()
199 return mime.icon_path
200 else :
201 return CMFFile.getIcon(self, relative_to_portal=relative_to_portal)
202
203
204 InitializeClass(File)
205 FileFactory = Factory(File)
206
207
208 def addFile( dispatcher
209 , id
210 , title=''
211 , file=''
212 , content_type=''
213 , precondition=''
214 , subject=()
215 , description=''
216 , contributors=()
217 , effective_date=None
218 , expiration_date=None
219 , format='text/html'
220 , language=''
221 , rights=''
222 ):
223 """
224 Add a File
225 """
226
227 # cookId sets the id and title if they are not explicity specified
228 id, title = OFS.Image.cookId(id, title, file)
229
230 container = dispatcher.Destination()
231
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
238 )
239
240 # Add the File instance to self
241 container._setObject(id, fobj)
242
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)