407 lines
14 KiB
Python
407 lines
14 KiB
Python
# -*- coding: iso-8859-15 -*-
|
|
|
|
|
|
# Glasnost
|
|
# By: Odile Bénassy <obenassy@entrouvert.com>
|
|
# Romain Chantereau <rchantereau@entrouvert.com>
|
|
# Nicolas Clapiès <nclapies@easter-eggs.org>
|
|
# Pierre-Antoine Dejace <padejace@entrouvert.be>
|
|
# Thierry Dulieu <tdulieu@easter-eggs.com>
|
|
# Florent Monnier <monnier@codelutin.com>
|
|
# Cédric Musso <cmusso@easter-eggs.org>
|
|
# Frédéric Péters <fpeters@entrouvert.be>
|
|
# Benjamin Poussin <poussin@codelutin.com>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
# Sébastien Régnier <regnier@codelutin.com>
|
|
# Emmanuel Saracco <esaracco@easter-eggs.com>
|
|
#
|
|
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
|
|
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
|
|
# Emmanuel Saracco & Théridion
|
|
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
|
|
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
|
|
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
|
|
# Régnier, Emmanuel Saracco, Théridion & Vecam
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
__doc__ = """Glasnost Upload Files Web"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import cStringIO
|
|
import copy
|
|
import zipfile
|
|
|
|
# From Python Imaging Library.
|
|
import Image as PILImage
|
|
|
|
import glasnost.common.context as context
|
|
import glasnost.common.faults as faults
|
|
import glasnost.common.slots as slots
|
|
|
|
from glasnost.proxy.UploadFilesProxy import *
|
|
|
|
from ObjectsWeb import register, AdminMixin, ObjectWebMixin, ObjectsWebMixin
|
|
from tools import *
|
|
|
|
|
|
class AdminUploadFiles(AdminMixin, AdminUploadFiles):
|
|
pass
|
|
register(AdminUploadFiles)
|
|
|
|
|
|
class UploadFile(ObjectWebMixin, UploadFile):
|
|
comment_kind_widget_fieldLabel = N_('Comment')
|
|
comment_kind_widget_cols = 40
|
|
comment_kind_widget_rows = 5
|
|
comment_kind_widgetName = 'TextArea'
|
|
|
|
data_kindName = 'UploadFile'
|
|
data_kind_widget_fieldLabel = N_('File')
|
|
data_kind_widget_fileName = None
|
|
data_kind_widgetName = 'UploadFile'
|
|
|
|
dataFileName_kind_stateInEditMode = 'read-only'
|
|
dataFileName_kind_widget_fieldLabel = N_('File Name')
|
|
dataFileName_kind_widget_size = 40
|
|
dataFileName_kind_widgetName = 'InputText'
|
|
|
|
dataType_kind_stateInEditMode = 'read-only'
|
|
dataType_kind_widget_fieldLabel = N_('Mime Type')
|
|
dataType_kind_widgetName = 'InputText'
|
|
|
|
height_kind_hasToRepairField = 0
|
|
height_kind_hasToSubmitField = 0
|
|
height_kind_stateInEditMode = 'read-only'
|
|
height_kind_min = 0
|
|
height_kind_textMaxLength = 4
|
|
height_kind_widget_fieldLabel = N_('Height')
|
|
height_kind_widget_size = 4
|
|
height_kind_widgetName = 'InputText'
|
|
|
|
size_kind_hasToSubmitField = 0
|
|
size_kind_stateInEditMode = 'read-only'
|
|
size_kind_min = 0
|
|
size_kind_textMaxLength = 6
|
|
size_kind_widget_fieldLabel = N_('Size')
|
|
size_kind_widget_size = 6
|
|
size_kind_widgetName = 'InputText'
|
|
|
|
title_kind_widget_fieldLabel = N_('Title')
|
|
title_kind_widget_size = 40
|
|
title_kind_widgetName = 'InputText'
|
|
|
|
width_kind_hasToRepairField = 0
|
|
width_kind_hasToSubmitField = 0
|
|
width_kind_stateInEditMode = 'read-only'
|
|
width_kind_min = 0
|
|
width_kind_textMaxLength = 4
|
|
width_kind_widget_fieldLabel = N_('Width')
|
|
width_kind_widget_size = 4
|
|
width_kind_widgetName = 'InputText'
|
|
|
|
def getEditLayoutSlotNames(self, fields, parentSlot = None):
|
|
slotNames = ObjectWebMixin.getViewLayoutSlotNames(
|
|
self, fields, parentSlot = parentSlot)
|
|
slotNames = slotNames[:]
|
|
hiddenSlotNames = []
|
|
for slotName in ('height', 'size', 'width'):
|
|
hiddenSlotNames.append(slotName)
|
|
if self.id is None:
|
|
for slotName in ('creationTime', 'dataFileName', 'dataType',
|
|
'modificationTime'):
|
|
hiddenSlotNames.append(slotName)
|
|
for slotName in hiddenSlotNames:
|
|
if slotName in slotNames:
|
|
slotNames.remove(slotName)
|
|
return slotNames
|
|
|
|
def getViewLayoutSlotNames(self, fields, parentSlot = None):
|
|
slotNames = ObjectWebMixin.getViewLayoutSlotNames(
|
|
self, fields, parentSlot = parentSlot)
|
|
slotNames = slotNames[:]
|
|
dataType = self.getSlot('dataType').getValue()
|
|
if not isTypeOfMimeType(dataType, 'image'):
|
|
if 'height' in slotNames:
|
|
slotNames.remove('height')
|
|
if 'width' in slotNames:
|
|
slotNames.remove('width')
|
|
userToken = context.getVar('userToken', default = '')
|
|
if not userToken or context.getVar('useCompactLayout', default = 0):
|
|
for slotName in [
|
|
'creationTime', 'dataFileName', 'dataType', 'height',
|
|
'size', 'modificationTime', 'readersSet', 'width',
|
|
'writersSet']:
|
|
if slotName in slotNames:
|
|
slotNames.remove(slotName)
|
|
return slotNames
|
|
|
|
def getViewLayout(self, fields, parentSlot = None):
|
|
if self.dataFileName:
|
|
slot = self.getSlot('data', parentSlot = parentSlot)
|
|
self.data_kind = copy.copy(self.data_kind) # Deleted below.
|
|
self.data_kind.widget = copy.copy(slot.getWidget())
|
|
self.data_kind.widget.fileName = self.dataFileName
|
|
layout = X.array()
|
|
if self.dataType == 'application/vnd.sun.xml.writer':
|
|
try:
|
|
import ooo
|
|
css, body = ooo.oo2html(self.data,
|
|
X.idUrl(self.id, 'zip').getAsUrl())
|
|
webTools.addContextualHeader(
|
|
'<style type="text/css">\n%s</style>\n' % css)
|
|
layout += X.asIs(body)
|
|
except ImportError:
|
|
pass
|
|
layout += ObjectWebMixin.getViewLayout(
|
|
self, fields, parentSlot = parentSlot)
|
|
if self.dataFileName:
|
|
del self.data_kind
|
|
return layout
|
|
|
|
def submitFields(self, fields, parentSlot = None):
|
|
ObjectWebMixin.submitFields(self, fields, parentSlot = parentSlot)
|
|
upload = context.getVar('UploadFilesXXX')
|
|
if upload:
|
|
self.dataFileName = upload.dataFileName
|
|
self.dataType = upload.dataType
|
|
register(UploadFile)
|
|
|
|
|
|
class UploadFilesWeb(ObjectsWebMixin, UploadFilesProxy):
|
|
def all(self):
|
|
return ObjectsWebMixin.viewAll(self)
|
|
all.isPublicForWeb = 1
|
|
|
|
#uploadFileFile = cStringIO.StringIO(data)
|
|
#uploadFileObject = PILImage.open(uploadFileFile)
|
|
#width, height = uploadFileObject.size
|
|
|
|
def image(self, id, fileName = None):
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
if not self.canGetObject(id):
|
|
return accessForbidden()
|
|
object = self.getPartialObject(
|
|
id, ['modificationTime', 'dataFileName'])
|
|
rememberObject(id)
|
|
|
|
if fileName and object.dataFileName != fileName:
|
|
pageNotFound()
|
|
if not fileName and object.dataFileName:
|
|
uri = X.idUrl(id, 'download/%s' % object.dataFileName)
|
|
return redirect(uri)
|
|
|
|
req = context.getVar('req')
|
|
req.depends.append(id)
|
|
object = self.getObject(id)
|
|
if object.dataFileName:
|
|
req.headers_out['Content-Disposition'] = \
|
|
'inline; filename="%s"' % object.dataFileName
|
|
req.headers_out['Content-Length'] = str(object.size or 0)
|
|
if object.dataType:
|
|
req.content_type = object.dataType
|
|
setHttpCookie()
|
|
req.send_http_header()
|
|
if req.method == 'HEAD':
|
|
return OK
|
|
if req.caching:
|
|
req.openCachePage()
|
|
req.write(object.data or '')
|
|
if req.caching:
|
|
req.closeCachePage()
|
|
return OK
|
|
image.isPublicForWeb = 1
|
|
|
|
download = image
|
|
download.isPublicForWeb = 1
|
|
|
|
def file(self, id):
|
|
try:
|
|
object = self.getObject(id)
|
|
except faults.FaultMissingItem:
|
|
return pageNotFound()
|
|
if not self.canModifyObject(id):
|
|
return accessForbidden()
|
|
req = context.getVar('req')
|
|
object.data = req.read()
|
|
if req.headers_in.has_key('Content-Type'):
|
|
object.dataType = req.headers_in['Content-Type']
|
|
else:
|
|
object.dataType = 'application/octet-stream'
|
|
self.modifyObject(object)
|
|
return HTTP_NO_CONTENT
|
|
file.isPublicForWeb = ('PUT',)
|
|
|
|
def imageEdit(self, dataToken, path = ''):
|
|
sessionsProxy = getProxyForServerRole('sessions')
|
|
data = sessionsProxy.getTemporaryData(dataToken)
|
|
|
|
req = context.getVar('req')
|
|
req.content_type = data['type']
|
|
setHttpCookie()
|
|
req.send_http_header()
|
|
if req.method == 'HEAD':
|
|
return OK
|
|
req.write(data['data'])
|
|
return OK
|
|
imageEdit.isPublicForWeb = 1
|
|
|
|
def thumbnail(self, id, width = '', height = ''):
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
if not self.canGetObject(id):
|
|
return accessForbidden()
|
|
try:
|
|
width = int(width)
|
|
except ValueError:
|
|
width = 128
|
|
try:
|
|
height = int(height)
|
|
except ValueError:
|
|
height = 128
|
|
object = self.getObjectThumbnail(
|
|
id, width, height)
|
|
rememberObject(id)
|
|
|
|
req = context.getVar('req')
|
|
req.content_type = object.dataType
|
|
setHttpCookie()
|
|
req.send_http_header()
|
|
if req.method == 'HEAD':
|
|
return OK
|
|
req.write(object.data)
|
|
return OK
|
|
thumbnail.isPublicForWeb = 1
|
|
|
|
def view(self, id):
|
|
if not self.hasObject(id):
|
|
return pageNotFound()
|
|
if not self.canGetObject(id):
|
|
return accessForbidden()
|
|
|
|
object = UploadFile()
|
|
slotNames = [x for x in object.getSlotNames() if x != 'data']
|
|
object = self.getPartialObject(id, slotNames)
|
|
# Force the presence of the download link in
|
|
# web/widgets.py/UploadFile.getHtmlViewValue().
|
|
# note that it fucks up previewing inline OpenOffice.org documents
|
|
# that could be converted to html
|
|
object.data = 'fake data'
|
|
if object.dataType == 'application/vnd.sun.xml.writer':
|
|
object = self.getObject(id)
|
|
rememberObject(id)
|
|
|
|
if isTypeOfMimeType(object.dataType, 'image'):
|
|
fullUri = X.idUrl(id, 'image')
|
|
else:
|
|
fullUri = X.idUrl(id, 'download')
|
|
uri = X.idUrl(id, 'thumbnail')
|
|
uri.add('width', 256)
|
|
uri.add('height', 256)
|
|
|
|
label = object.getLabelTranslated(context.getVar('readLanguages'))
|
|
|
|
layout = X.array()
|
|
slot = slots.Root(object)
|
|
widget = slot.getWidget()
|
|
layout += widget.getModelPageBodyLayout(slot, None)
|
|
layout += self.getViewAboveButtonsBarLayout(object, None)
|
|
layout += self.getViewButtonsBarLayout(object, None)
|
|
layout += self.getViewBelowButtonsBarLayout(object, None)
|
|
return writePageLayout(
|
|
layout, '%s - %s' % (_(self.objectNameCapitalized), label))
|
|
view.isPublicForWeb = 1
|
|
|
|
def viewAll(self):
|
|
context.push(_level = 'viewAll',
|
|
defaultDispatcherId = context.getVar('dispatcherId'))
|
|
try:
|
|
userId = context.getVar('userId', default = '')
|
|
if not self.canGetObjects():
|
|
return accessForbidden()
|
|
isAdmin = self.isAdmin()
|
|
if userId:
|
|
userSet = [userId]
|
|
else:
|
|
userSet = None
|
|
|
|
layout = X.array()
|
|
requiredSlotNames = ['dataType', 'size', 'title']
|
|
displayedSlotNames = ['dataType', 'size']
|
|
|
|
if userSet:
|
|
lastUploadFiles = self.getLastObjects(
|
|
10, None, userSet, requiredSlotNames)
|
|
layout += self.getObjectsSectionLayout(
|
|
lastUploadFiles,
|
|
_("""Your last files"""),
|
|
displayedSlotNames)
|
|
|
|
lastUploadFiles = self.getLastObjects(
|
|
10, userSet, None, requiredSlotNames)
|
|
layout += self.getObjectsSectionLayout(
|
|
lastUploadFiles,
|
|
_("""The last files"""),
|
|
displayedSlotNames)
|
|
|
|
layout += self.getViewAllButtonsBarLayout()
|
|
finally:
|
|
context.pull(_level = 'viewAll')
|
|
return writePageLayout(layout, _('Files'))
|
|
viewAll.isPublicForWeb = 1
|
|
|
|
def zip(self, id, *filepath):
|
|
filepath = '/'.join(filepath)
|
|
object = self.getObject(id)
|
|
fd = cStringIO.StringIO(object.data)
|
|
zf = zipfile.ZipFile(fd)
|
|
|
|
data = zf.read(filepath)
|
|
|
|
req = context.getVar('req')
|
|
|
|
# TODO: function that gets contentType from extension (or better)
|
|
if filepath.endswith('.jpg'):
|
|
req.content_type = 'image/jpeg'
|
|
else:
|
|
req.content_type = 'application/octet-stream'
|
|
req.depends.append(id)
|
|
object = self.getObject(id)
|
|
if object.dataFileName:
|
|
req.headers_out['Content-Disposition'] = \
|
|
'inline; filename="%s"' % filepath.split('/')[-1]
|
|
req.headers_out['Content-Length'] = str(len(data))
|
|
setHttpCookie()
|
|
req.send_http_header()
|
|
if req.method == 'HEAD':
|
|
return OK
|
|
if req.caching:
|
|
req.openCachePage()
|
|
req.write(data)
|
|
if req.caching:
|
|
req.closeCachePage()
|
|
return OK
|
|
|
|
|
|
pass
|
|
zip.isPublicForWeb = 1
|
|
|