This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
glasnost/shared/web/UploadFilesWeb.py

436 lines
16 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_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'
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'
def getEditLayoutSlotNames(self, fields, parentSlot = None):
slotNames = ObjectWebMixin.getViewLayoutSlotNames(
self, fields, parentSlot = parentSlot)
slotNames = slotNames[:]
hiddenSlotNames = []
for slotName in ['size', 'properties'] + self.getPropertyNames():
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()
userToken = context.getVar('userToken', default = '')
if 'properties' in slotNames:
slotNames.remove('properties')
if context.getVar('useCompactLayout', default = 0) or \
not self.getWeb().canModifyObject(self.id):
for slotName in [
'creationTime', 'dataFileName', 'dataType',
'modificationTime', 'readersSet', '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
except:
pass
if self.size:
origSize = self.size
self.size = '%.1f Ko' % (self.size/1024.)
layout += ObjectWebMixin.getViewLayout(
self, fields, parentSlot = parentSlot)
if self.size:
self.size = origSize
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):
context.push(_level = 'viewAll',
defaultDispatcherId = context.getVar('dispatcherId'))
try:
userId = context.getVar('userId', default = '')
if not self.canGetObjects():
return accessForbidden()
isAdmin = self.isAdmin()
layout = X.array()
ids = self.getObjectIds()
partialObjects = {}
for id in ids:
partialObjects[id] = self.getPartialObject(
id, ['title', 'dataType', 'size'])
ids = self.getSortedIds(partialObjects)
layout += self.getObjectsLayout(partialObjects, ids,
slotNames = ['dataType', 'size'])
layout += self.getViewAllButtonsBarLayout()
finally:
context.pull(_level = 'viewAll')
return writePageLayout(layout,
self.getTranslatedObjectsNameCapitalized())
all.isPublicForWeb = 1
def getViewAllNavigationButtonsBarLayoutSomeTimes(self):
layout = X.array()
layout += X.buttonStandalone('every-uploadfiles', X.actionUrl('all'))
layout += ObjectsWebMixin.getViewAllNavigationButtonsBarLayout(self)
return layout
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 != urllib.unquote(fileName):
return 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
# voodoo solution so that the right filename is used in MSIE
# (from <http://groups.google.com/groups?selm=
# pan.2003.12.11.14.18.31.315803%40ezglkhzglihzrhg.com>)
req.headers_out['Cache-Control'] = ''
req.headers_out['Pragma'] = ''
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
try:
object = self.getObjectThumbnail(id, width, height)
except faults.UnresizableImage:
return pageNotFound() # FIXME: why not HTTP_NOT_IMPLEMENTED
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)
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.getTranslatedObjectNameCapitalized(), 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)
# ArticlesWeb did that; I don't remember why.
self.getViewAllNavigationButtonsBarLayout = \
self.getViewAllNavigationButtonsBarLayoutSomeTimes
layout += self.getViewAllButtonsBarLayout()
del self.getViewAllNavigationButtonsBarLayout
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'] = \
'attachment; filename="%s"' % filepath.split('/')[-1]
# voodoo solution so that the right filename is used in MSIE
# (from <http://groups.google.com/groups?selm=
# pan.2003.12.11.14.18.31.315803%40ezglkhzglihzrhg.com>)
req.headers_out['Cache-Control'] = ''
req.headers_out['Pragma'] = ''
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