# -*- coding: utf-8 -*-
## AttchmentField
## Copyright (C)2006 Ingeniweb

## 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; see the file COPYING. If not, write to the
## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""
AttchmentField
"""
__version__ = "$Revision: 23491 $"
# $Source: /cvsroot/ingeniweb/PloneSubscription/SubscriptionTool.py,v $
# $Id: AttachmentField.py 23491 2006-05-15 13:44:47Z glenfant $
__docformat__ = 'restructuredtext'

import urllib
import string
import os
import os.path
import sys
from types import FileType, ListType, TupleType

import Acquisition
from Acquisition import aq_base
from Globals import Persistent
from Globals import MessageDialog, DTMLFile      # fakes a method from a DTML file
from Globals import InitializeClass
from AccessControl import Role
from AccessControl import ClassSecurityInfo
from AccessControl import Permissions
from AccessControl import Unauthorized
from AccessControl import getSecurityManager
from webdav.common import rfc1123_date
import OFS.SimpleItem
from OFS.ObjectManager import ObjectManager
from OFS.Traversable import Traversable
from OFS.Image import Pdata, File
from OFS.content_types import guess_content_type
from global_symbols import *
try:
    from Products.CMFCore.utils import getToolByName
except:
    pass                # No CMF -> no charset converting

from Products.Archetypes import Field
from Products.Archetypes.utils import shasattr
from Products.Archetypes.interfaces.base import IBaseUnit

from Products.AttachmentField.interfaces.attachmentfield import IAttachmentField
import AttachmentHandler

##DEFAULT_ID = "attach"
##ZAA_READ_PERMISSION = Permissions.access_contents_information
##ZAA_WRITE_PERMISSION = Permissions.change_images_and_files

#if _AF_VOLATILE_:
#    _indexed_ = "_v_%s_AF_indexed"
#    _preview_ = "_v_%s_AF_preview"
#else:
#    _indexed_ = "_%s_AF_indexed"
#    _preview_ = "_%s_AF_preview"

_indexed_ = "_%s_AF_indexed"
_preview_ = "_%s_AF_preview"

class AttachmentField(Field.FileField):
    """
    A base class to handle file fields. This is based on Archetypes.
    When the file is uploaded, it's stored, as the File field, as a File class.

    See FileField.set() :
    
        value = File(self.getName(), '', value, mimetype)
        setattr(value, 'filename', f_name or self.getName())
        ObjectField.set(self, instance, value, **kwargs)

    """
    __implements__ = (Field.FileField.__implements__, IAttachmentField)
    security = ClassSecurityInfo()

    def get(self, instance, mimetype = None, **kwargs):
        """Get value. If mime_type is 'text/plain', we retreive the
        indexed string. If it's text/html, we get the preview back.
        """
        if mimetype == 'text/plain':
            return self.getIndexableValue(instance)
        if mimetype == 'text/html':
            return self.getPreview(instance)
        kwargs.update({'mimetype': mimetype})
        return Field.FileField.get(self, instance, **kwargs)

    def set(self, instance, value, **kwargs):
        """
        Assign input value to object. If mimetype is not specified,
        pass to processing method without one and add mimetype returned
        to kwargs. Assign kwargs to instance.
        """
        self._reset(instance)
        return Field.FileField.set(self, instance, value, **kwargs)

    security.declarePrivate('download')
    def download(self, instance, REQUEST=None, RESPONSE=None):
        """Use storage download method or field one"""
        
        obj = self.get(instance)
        
        if obj is not None and hasattr(aq_base(obj), 'download'):
            return obj.download(REQUEST)
        else:
            return Field.FileField.download(self, instance, REQUEST, RESPONSE)

##    def _process_input(self, value, default=None,
##                       mimetype=None, **kwargs):
##        # We also need to handle the case where there is a baseUnit
##        # for this field containing a valid set of data that would
##        # not be reuploaded in a subsequent edit, this is basically
##        # migrated from the old BaseObject.set method
##        from ZPublisher.HTTPRequest import FileUpload
##        from ZODB.POSException import ConflictError
##        if not (isinstance(value, FileUpload) or type(value) is FileType) \
##          and shasattr(value, 'read') and shasattr(value, 'seek'):
##            # support StringIO and other file like things that aren't either
##            # files or FileUploads
##            value.seek(0) # rewind
##            kwargs['filename'] = getattr(value, 'filename', '')
##            mimetype = getattr(value, 'mimetype', None)
##            value = value.read()
##        if isinstance(value, Pdata):
##            # Pdata is a chain of Pdata objects but we can easily use str()
##            # to get the whole string from a chain of Pdata objects
##            value = str(value)
##        if type(value) in Field.STRING_TYPES:
##            filename = kwargs.get('filename', '')
##            if mimetype is None:
##                mimetype, enc = guess_content_type(filename, value, mimetype)
##            if not value:
##                return default, mimetype, filename
##            return value, mimetype, filename
##        elif IBaseUnit.isImplementedBy(value):
##            return value.getRaw(), value.getContentType(), value.getFilename()

##        value = aq_base(value)

##        if ((isinstance(value, FileUpload) and value.filename != '') or
##              (type(value) is FileType and value.name != '')):
##            filename = ''
##            if isinstance(value, FileUpload) or shasattr(value, 'filename'):
##                filename = value.filename
##            if isinstance(value, FileType) or shasattr(value, 'name'):
##                filename = value.name
##            # Get only last part from a 'c:\\folder\\file.ext'
##            filename = filename.split('\\')[-1]
##            value.seek(0) # rewind
##            value = value.read()
##            if mimetype is None:
##                mimetype, enc = guess_content_type(filename, value, mimetype)
##            size = len(value)
##            if size == 0:
##                # This new file has no length, so we keep the orig
##                return default, mimetype, filename
##            else:
##                return value, mimetype, filename

##        # wrap VirtualFile object
##        if hasattr(value, '__of__'):
##            value = value.__of__(value)
##        if isinstance(value, File):
##            # OFS.Image.File based
##            filename = value.filename
##            mimetype = value.content_type
##            data = value.data
##            if len(data) == 0:
##                return default, mimetype, filename
##            else:
##                return data, mimetype, filename

##        klass = getattr(value, '__class__', None)
##        raise FileFieldException('Value is not File or String (%s - %s)' %
##                                 (type(value), klass))

    security.declarePublic("getFilename")
    def getFilename(self, instance, fromBaseUnit=True):
        """
        getFilename(self, instance, ) => return the filename
        We split to return only a file name and not a whole path.
        """
        
        # Get object or data
        accessor = self.getAccessor(instance)
        
        if self.isEmpty(instance) or accessor is None:
            return ''
        
        obj = accessor()
        fn = ''
        
        if hasattr(obj.aq_explicit, 'filename'):
            fn = obj.filename
        elif hasattr(obj.aq_explicit, 'getFilename'):
            fn = obj.getFilename()
        else:
            fn = Field.FileField.getFilename(self, instance, fromBaseUnit)
            
        fn = string.split(fn, '/')[-1]
        fn = string.split(fn, '\\')[-1]
        return fn

    def guessMimeTypeFromContent(self, instance, ):
        """
        guessMimeType(self, instance) => guess a mime type using mimetypes registry,
        based on the content itself.
        """
        value = str(self.get(instance))
        mtr = getToolByName(instance, 'mimetypes_registry')
        mimetype = mtr.classify(value, filename=self.getFilename(instance).lower())
        if not mimetype:
            return self.default_content_type
        return str(mimetype)


##    def getContentType(self, instance, fromBaseUnit=True):
##        """
##        getContentType(self, instance, fromBaseUnit=True) => return the content type
##        """
##        # Get object or data
##        accessor = self.getAccessor(instance)

##        # Empty object : we use the default content type
##        if self.isEmpty(instance) or accessor is None:
##            Log(LOG_DEBUG, "returning defaut content type", self.default_content_type)
##            return self.default_content_type

##        # Look at the underlying structure
##        obj = accessor()
##        if hasattr(obj.aq_explicit, 'mimetype'):
##            ret = obj.mimetype
##        elif hasattr(obj.aq_explicit, 'getContentType'):
##            ret = obj.getContentType()
##        else:
##            ret = Field.FileField.getContentType(self, instance, fromBaseUnit)
##        Log(LOG_DEBUG, "Mimetype before correction: ", ret)

##        # Correct the type we guessed if needed
##        if ret in ("application/octet-stream", 'text/x-unknown-content-type', ):
##            ret = self.guessMimeTypeFromContent(instance)
##        Log(LOG_DEBUG, "After correction, it's...", ret)

##        return ret

    def getSize(self, instance):
        """
        getSize(self, instance) => return file size
        """
        if self.isEmpty(instance):
            return 0
        return self.get(instance).get_size()    # This is cached by OFS.File

    def isEmpty(self, instance):
        """
        return true if empty
        """
        instance = self.get(instance)
        if instance is None:
            return True
        if type(instance) in (type(''), type(u'')):
            return not len(instance)
        size = 0
        try:
            size = instance.get_size()
        except:
            pass
        return not size

    def getIndexableValue(self, instance):
        """
        getIndexableValue(self, instance) => Return the value we have to index
        """
        # Emptyness check
        if self.isEmpty(instance):
            return ""

        # Is the indexing up-to-date ?
        name = self.getName()
        if not getattr(instance, _indexed_ % name, None):
            handler = self._getHandler(instance)
            try:
                idx = handler.getIndexableValue(self, instance)
            except:
                self._logException(instance)
                idx = None
            setattr(instance, _indexed_ % name, idx)

        # Return the index
        ret = getattr(instance, _indexed_ % name, None)
        return ret

    def isIndexed(self, instance):
        """return true if the document is indexed properly.
        """
        if self.isEmpty(instance):
            return None
        return not not self.getIndexableValue(instance)

    def isPreviewAvailable(self, instance):
        """True if a preview is available for that
        (we can compute it immediately)
        """
        return not not self.getPreview(instance)

    def isMultiValued(self, instance):
        """True if field is multi valued
        """
        return isinstance(self.get(instance), (ListType, TupleType))

    def _getHandler(self, instance):
        """
        _getHandler(self, instance) => get the handler object
        """
        handler = AttachmentHandler.getAttachmentHandler(
            self.getContentType(instance, ),
            self, instance,
            )
        return handler


    def getPreview(self, instance):
        """Return the preview for this object
        """
        if self.isEmpty(instance):
            return ""

        # Compute it if necessary
        name = self.getName()
        if not getattr(instance, _preview_ % name, None):
            handler = self._getHandler(instance)
            try:
                preview = handler.getPreview(self, instance, )
            except:
                self._logException(instance)
                preview = None
            setattr(instance, _preview_ % name, preview, )

        # Return it
        return getattr(instance, _preview_ % name, None)
    
    def getIcon(self, instance):
        """
        getIcon(self, instance) => return the underlying file class icon (object)
        """
        handler = self._getHandler(instance)
        Log(LOG_DEBUG, "getIcon for", self.getFilename(instance), "/", handler.converter_type, )
        return getattr(instance, handler.getIconFile(self, instance))

    def getSmallIcon(self, instance):
        """
        getIcon(self, instance) => return the underlying file class icon (object)
        """
        handler = self._getHandler(instance)
        return getattr(instance, handler.getSmallIconFile(self, instance))

    def _reset(self, instance, ):
        """reset volatile stuff (indexation, preview)
        """
        name = self.getName()
        setattr(instance, _preview_ % name, None)
        setattr(instance, _indexed_ % name, None)

    def _logException(self, instance):
        if instance and hasattr(instance, 'getPhysicalPath'):
            path = '/'.join(instance.getPhysicalPath())
            filename = self.getFilename(instance)
            msg = 'EXCEPTION object: %s, file: %s: \n' % (path, filename)
            Log(LOG_NOTICE, msg)
        LogException()

InitializeClass(AttachmentField)

from Products.Archetypes.Registry import registerField

registerField(
    AttachmentField,
    title='Attachment',
    description='Used for storing files with advanced features.',
    )

    

