# -*- coding: utf-8 -*-
## AttachmentField
## 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.
"""
AttachmentField
"""
__version__ = "$Revision: 65264 $"
# $Id: AttachmentField.py 65264 2008-05-20 14:27:22Z encolpe $
__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 Products.AttachmentField import LOG
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.Archetypes.BaseUnit import BaseUnit
 
from Products.AttachmentField.interfaces.attachmentfield import IAttachmentField
import AttachmentHandler
 
from FlexStorage import FlexStorage
 
##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"
_isindexed_ = "_%s_AF_isindexed"
_ispreview_ = "_%s_AF_ispreview"
_icon_ = "_%s_AF_icon"
_smallicon_ = "_%s_AF_smallicon"
 
# Zope 2.7 compatibility
try:
    import transaction
    savepoint = transaction.savepoint
except ImportError:
    def savepoint(**kwargs):
        get_transaction().commit(1)
 
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()
 
 
    _properties = Field.FileField._properties.copy()
    _properties.update({
            "storage": FlexStorage()
        })
 
 
    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.
        """
        ## ZEE PART 1 BEGIN
        ## XXX Here a patch for editing with Zope External Editor with all 0.9.x
        ## versions
        ## ZEE looses filename when editing and replace it by the id.
        ## This works as long as the id is not chosen or modified by an user
        request = kwargs.get('REQUEST', None)
        filename = ''
        is_bad_user_agent = False
        if request is not None:
            user_agent = request.get('HTTP_USER_AGENT', '')
            if user_agent.lower().startswith('zope external editor'):
                is_bad_user_agent = True
                filename = self.getFilename(instance)
        ## ZEE PART 1 END
 
        self._reset(instance)
        ret = Field.FileField.set(self, instance, value, **kwargs)
 
        ## ZEE PART 2 BEGIN
        ## Set the filename given before the ZEE commit
        if is_bad_user_agent:
            self.setFilename(instance, filename)
        ## ZEE PART 2 END
 
        return ret
 
    def getSize(self, instance):
        """
            getSize(self, instance) => return file size
 
            This method should be deprecated: now, use get_size(), as seen in
            FileField.
 
        """
        return self.get_size(instance)
 
    def isEmpty(self, instance):
        """
        return true if empty
        """
        file = self.get(instance)
        if file is None:
            return True
        if type(file) in (type(''), type(u'')):
            return not len(file)
        size = 0
        try:
            size = file.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()
        isindexed = hasattr(instance, _isindexed_ % name)
 
        if not isindexed:
            handler = self._getHandler(instance)
            idx = None
            try:
                idx = handler.getIndexableValue(self, instance)
                if idx:
                    setattr(instance, _indexed_ % name, idx)
                    setattr(instance, _isindexed_ % name, True)
            except:
                self._logException(instance)
            if not idx:
                setattr(instance, _indexed_ % name, None)
                setattr(instance, _isindexed_ % name, False)
 
        # Return it
        return getattr(instance, _indexed_ % name, None)
 
    getIndexable = getIndexableValue
 
    def isIndexed(self, instance):
        """return true if the document is indexed properly.
        """
 
        name = self.getName()
        if hasattr(instance, _isindexed_ % name):
            return getattr(instance, _isindexed_ % name, False)
        else:
            return not not self.getIndexableValue(instance)
 
    def isPreviewAvailable(self, instance):
        """True if a preview is available for that
        (we can compute it immediately)
        """
 
        name = self.getName()
        if hasattr(instance, _ispreview_ % name):
            return getattr(instance, _ispreview_ % name, False)
        else:
            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()
        ispreview = hasattr(instance, _ispreview_ % name)
 
        if not ispreview:
            handler = self._getHandler(instance)
            preview = None
            try:
                preview = handler.getPreview(self, instance, )
                if preview:
                    setattr(instance, _preview_ % name, preview, )
                    setattr(instance, _ispreview_ % name, True, )
            except:
                self._logException(instance)
 
            if not preview:
                setattr(instance, _preview_ % name, None )
                setattr(instance, _ispreview_ % name, False, )
 
        # Return it
        return getattr(instance, _preview_ % name, None)
 
 
    def getIcon(self, instance):
        """
        getIcon(self, instance) => return the underlying file class icon (object)
        """
 
        name = self.getName()
        icon = getattr(instance, _icon_ % name, None)
 
        if not icon:
            handler = self._getHandler(instance)
            LOG.debug("getIcon for %s / %s" % (self.getFilename(instance),
                                               handler.converter_type, ))
            icon = handler.getIconFile(self, instance)
            setattr(instance, _icon_ % name, icon, )
        return getattr(instance, icon, None)
 
    def getSmallIcon(self, instance):
        """
        getIcon(self, instance) => return the underlying file class icon (object)
        """
 
        name = self.getName()
        smallicon = getattr(instance, _smallicon_ % name, None)
 
        if not smallicon:
            handler = self._getHandler(instance)
            smallicon = handler.getSmallIconFile(self, instance)
            setattr(instance, _smallicon_ % name, smallicon, )
        return getattr(instance, smallicon, None)
 
    def _reset(self, instance, ):
        """reset volatile stuff (indexation, preview)
        """
        name = self.getName()
        setattr(instance, _preview_ % name, None)
        setattr(instance, _indexed_ % name, None)
        setattr(instance, _ispreview_ % name, None)
        setattr(instance, _isindexed_ % name, None)
        setattr(instance, _icon_ % name, None)
        setattr(instance, _smallicon_ % name, None)
        delattr(instance, _isindexed_ % name)
        delattr(instance, _ispreview_ % name)
 
    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.warning(msg, exc_info=True)
        else:
            LOG.warning('Exception occured', exc_info=True)
 
InitializeClass(AttachmentField)
 
from Products.Archetypes.Registry import registerField
 
registerField(
    AttachmentField,
    title='Attachment',
    description='Used for storing files with advanced features.',
)