# -*- 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

