979148d00640fbb7e658a9ff13a92ca1500c37f4
[Plinn.git] / AttachmentTool.py
1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # © 2007-2014 Benoît Pin <pin@cri.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 """ Portal attachment management tool.
21
22
23
24 """
25
26 from AccessControl import ClassSecurityInfo
27 from Acquisition import aq_base
28 from Globals import InitializeClass
29 from OFS.SimpleItem import SimpleItem
30 from OFS.Folder import Folder
31 from OFS.Image import File, cookId
32 from zExceptions import Unauthorized
33 from zExceptions import BadRequest
34 from Products.Photo import Photo
35 from Products.CMFCore.utils import UniqueObject, getToolByName, getUtilityByInterfaceName
36 from Products.CMFCore.permissions import ModifyPortalContent
37 from Products.CMFCore.exceptions import AccessControl_Unauthorized
38 from Products.Plinn.utils import makeValidId
39
40 from urllib import unquote
41 from cgi import escape
42 from ZServer import LARGE_FILE_THRESHOLD
43 from webdav.interfaces import IWriteLock
44 from webdav.common import Locked
45 from webdav.common import PreconditionFailed
46 from zope.contenttype import guess_content_type
47
48
49
50 class AttachmentTool( UniqueObject, SimpleItem):
51 """ Links attachment objects to contents.
52 """
53
54 id = 'portal_attachment'
55 meta_type = 'Attachment Tool'
56 manage_options = SimpleItem.manage_options
57
58 security = ClassSecurityInfo()
59
60 security.declarePublic('getAttachmentsFor')
61 def getAttachmentsFor(self, content):
62 """getAttachmentsFor returns attachments container of content
63 """
64 if getattr( aq_base(content), 'attachments', None ) is None :
65 self._createAttachmentContainerFor(content)
66
67 return content.attachments
68
69 security.declarePrivate('_createAttachmentContainerFor')
70 def _createAttachmentContainerFor(self, content):
71 """_createAttachmentContainerFor documentation
72 """
73
74 content.attachments = AttachmentContainer()
75
76 security.declarePublic('uploadAttachmentFor')
77 def uploadAttachmentFor(self, content, file, title='', typeName='File') :
78 "upload attachment inside content's attachment folder."
79
80 mtool = getToolByName(self, 'portal_membership')
81 if not mtool.checkPermission(ModifyPortalContent, content) :
82 raise AccessControl_Unauthorized
83
84 utool = getToolByName(self, 'portal_url')
85 portal = utool.getPortalObject()
86
87 attachments = self.getAttachmentsFor(content)
88 dummy, title = cookId('', title, file)
89 id = makeValidId(attachments, title)
90
91 if typeName == 'Photo':
92 thumbSize = {'thumb_height' : portal.getProperty('thumb_size', 192),
93 'thumb_width' : portal.getProperty('thumb_size', 192)}
94 fileOb = Photo(id, title, file, **thumbSize)
95 elif typeName == 'File' :
96 fileOb = File(id, title, '')
97 fileOb.manage_upload(file)
98 else :
99 raise AccessControl_Unauthorized
100
101 content.attachments._setObject(id, fileOb)
102 fileOb = getattr(content.attachments, id)
103 return fileOb
104
105
106
107 InitializeClass( AttachmentTool )
108
109
110 class AttachmentContainer (Folder):
111
112 meta_type = 'Attachment container'
113 security = ClassSecurityInfo()
114
115 def __init__(self):
116 self.id = 'attachments'
117
118 security.declarePrivate('checkIdAvailable')
119 def checkIdAvailable(self, id):
120 try:
121 self._checkId(id)
122 except BadRequest:
123 return False
124 else:
125 return True
126
127
128 security.declareProtected(ModifyPortalContent, 'put_upload')
129 def put_upload(self, REQUEST, RESPONSE):
130 """ Upload a content thru webdav put method.
131 The default behavior (NullRessource.PUT + PortalFolder.PUT_factory)
132 disallow files names with '_' at the begining.
133 """
134
135 self.dav__init(REQUEST, RESPONSE)
136 fileName = unquote(REQUEST.getHeader('X-File-Name', ''))
137 validId = makeValidId(self, fileName, allow_dup=True)
138
139 ifhdr = REQUEST.get_header('If', '')
140 if self.wl_isLocked():
141 if ifhdr:
142 self.dav__simpleifhandler(REQUEST, RESPONSE, col=1)
143 else:
144 raise Locked
145 elif ifhdr:
146 raise PreconditionFailed
147
148 if int(REQUEST.get('CONTENT_LENGTH') or 0) > LARGE_FILE_THRESHOLD:
149 file = REQUEST['BODYFILE']
150 body = file.read(LARGE_FILE_THRESHOLD)
151 file.seek(0)
152 else:
153 body = REQUEST.get('BODY', '')
154
155 typ=REQUEST.get_header('content-type', None)
156 if typ is None:
157 typ, enc=guess_content_type(validId, body)
158
159 if self.checkIdAvailable(validId) :
160 if typ.startswith('image/') :
161 utool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IURLTool')
162 portal = utool.getPortalObject()
163 thumbSize = {'thumb_height' : portal.getProperty('thumb_size', 192),
164 'thumb_width' : portal.getProperty('thumb_size', 192)}
165 ob = Photo(validId, fileName, '', **thumbSize)
166 else :
167 ob = File(validId, fileName, '')
168
169 self._setObject(validId, ob)
170 httpRespCode = 201
171 else :
172 httpRespCode = 200
173 ob = self._getOb(validId)
174
175 # We call _verifyObjectPaste with verify_src=0, to see if the
176 # user can create this type of object (and we don't need to
177 # check the clipboard.
178 try:
179 self._verifyObjectPaste(ob.__of__(self), 0)
180 except CopyError:
181 sMsg = 'Unable to create object of class %s in %s: %s' % \
182 (ob.__class__, repr(self), sys.exc_info()[1],)
183 raise Unauthorized, sMsg
184
185 ob.PUT(REQUEST, RESPONSE)
186 RESPONSE.setStatus(httpRespCode)
187 RESPONSE.setHeader('Content-Type', 'text/xml;;charset=utf-8')
188 if ob.meta_type == 'File' :
189 return '<element id="%s" title="%s"/>' % (ob.getId(), escape(ob.title_or_id()))
190 elif ob.meta_type == 'Photo' :
191 width, height = ob.getResizedImageSize(size=(310, 310))
192 return '<element src="%(src)s" title="%(title)s" width="%(width)d" height="%(height)d"/>' % \
193 {'src' : 'attachments/%s/getResizedImage?size=%d_%d' % (ob.getId(), width, height),
194 'title' : escape(ob.title_or_id()),
195 'width' : width,
196 'height' : height
197 }
198
199
200 InitializeClass(AttachmentContainer)