# -*- coding: utf-8 -*-
#######################################################################################
#   Plinn - http://plinn.org                                                          #
#   © 2007-2014  Benoît Pin <pin@cri.ensmp.fr>                                        #
#                                                                                     #
#   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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.   #
#######################################################################################
""" Portal attachment management tool.



"""

from AccessControl import ClassSecurityInfo
from Acquisition import aq_base
from Globals import InitializeClass
from OFS.SimpleItem import SimpleItem
from OFS.Folder import Folder
from OFS.Image import cookId
from Products.Photo.blobbases import File
from zExceptions import Unauthorized
from zExceptions import BadRequest
from Products.Photo import Photo
from Products.CMFCore.utils import UniqueObject, getToolByName, getUtilityByInterfaceName
from Products.CMFCore.permissions import ModifyPortalContent
from Products.CMFCore.exceptions import AccessControl_Unauthorized
from Products.Plinn.utils import makeValidId

from urllib import unquote
from cgi import escape
from ZServer import LARGE_FILE_THRESHOLD
from webdav.interfaces import IWriteLock
from webdav.common import Locked
from webdav.common import PreconditionFailed
from zope.contenttype import guess_content_type

from libxml2 import HTML_PARSE_RECOVER, HTML_PARSE_NOERROR, HTML_PARSE_NOWARNING
from libxml2 import htmlReadDoc
PARSE_OPTIONS = HTML_PARSE_RECOVER + HTML_PARSE_NOERROR + HTML_PARSE_NOWARNING

class AttachmentTool( UniqueObject, SimpleItem):
    """ Links attachment objects to contents.
    """

    id = 'portal_attachment'
    meta_type = 'Attachment Tool'
    manage_options = SimpleItem.manage_options

    security = ClassSecurityInfo()

    security.declarePublic('getAttachmentsFor')
    def getAttachmentsFor(self, content):
        """getAttachmentsFor returns attachments container of content
        """
        if getattr( aq_base(content), 'attachments', None ) is None :
            self._createAttachmentContainerFor(content)
        
        return content.attachments
    
    security.declarePrivate('_createAttachmentContainerFor')
    def _createAttachmentContainerFor(self, content):
        """_createAttachmentContainerFor documentation
        """
        
        content.attachments = AttachmentContainer()
    
    security.declarePublic('uploadAttachmentFor')
    def uploadAttachmentFor(self, content, file, title='', typeName='File') :
        "upload attachment inside content's attachment folder."
        
        mtool = getToolByName(self, 'portal_membership')
        if not mtool.checkPermission(ModifyPortalContent, content) :
            raise AccessControl_Unauthorized
        
        utool = getToolByName(self, 'portal_url')
        portal = utool.getPortalObject()
        
        attachments = self.getAttachmentsFor(content)
        dummy, title = cookId('', title, file)
        id = makeValidId(attachments, title)
        
        if typeName == 'Photo':
            thumbSize = {'thumb_height' : portal.getProperty('thumb_size', 192),
                         'thumb_width'  : portal.getProperty('thumb_size', 192)}
            fileOb = Photo(id, title, file, **thumbSize)
        elif typeName == 'File' :
            fileOb = File(id, title, '')
            fileOb.manage_upload(file)
        else :
            raise AccessControl_Unauthorized

        content.attachments._setObject(id, fileOb)
        fileOb = getattr(content.attachments, id)
        return fileOb
        
        

InitializeClass( AttachmentTool )


class AttachmentContainer (Folder):
    
    meta_type = 'Attachment container'
    security = ClassSecurityInfo()
    
    def __init__(self):
        self.id = 'attachments'

    security.declarePrivate('checkIdAvailable')
    def checkIdAvailable(self, id):
        try:
            self._checkId(id)
        except BadRequest:
            return False
        else:
            return True


    security.declareProtected(ModifyPortalContent, 'put_upload')
    def put_upload(self, REQUEST, RESPONSE):
        """ Upload a content thru webdav put method.
            The default behavior (NullRessource.PUT + PortalFolder.PUT_factory)
            disallow files names with '_' at the begining.
        """

        self.dav__init(REQUEST, RESPONSE)
        fileName = unquote(REQUEST.getHeader('X-File-Name', ''))
        validId = makeValidId(self, fileName, allow_dup=True)

        ifhdr = REQUEST.get_header('If', '')
        if self.wl_isLocked():
            if ifhdr:
                self.dav__simpleifhandler(REQUEST, RESPONSE, col=1)
            else:
                raise Locked
        elif ifhdr:
            raise PreconditionFailed

        if int(REQUEST.get('CONTENT_LENGTH') or 0) > LARGE_FILE_THRESHOLD:
            file = REQUEST['BODYFILE']
            body = file.read(LARGE_FILE_THRESHOLD)
            file.seek(0)
        else:
            body = REQUEST.get('BODY', '')

        typ=REQUEST.get_header('content-type', None)
        if typ is None:
            typ, enc=guess_content_type(validId, body)

        if self.checkIdAvailable(validId) :
            if typ.startswith('image/') :
                utool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IURLTool')
                portal = utool.getPortalObject()
                thumbSize = {'thumb_height' : portal.getProperty('thumb_size', 192),
                             'thumb_width'  : portal.getProperty('thumb_size', 192)}
                ob = Photo(validId, fileName, '', **thumbSize)
            else :
                ob = File(validId, fileName, '')
            
            self._setObject(validId, ob)
            httpRespCode = 201
        else :
            httpRespCode = 200
            ob = self._getOb(validId)

        # We call _verifyObjectPaste with verify_src=0, to see if the
        # user can create this type of object (and we don't need to
        # check the clipboard.
        try:
            self._verifyObjectPaste(ob.__of__(self), 0)
        except CopyError:
             sMsg = 'Unable to create object of class %s in %s: %s' % \
                    (ob.__class__, repr(self), sys.exc_info()[1],)
             raise Unauthorized, sMsg

        ob.PUT(REQUEST, RESPONSE)
        RESPONSE.setStatus(httpRespCode)
        RESPONSE.setHeader('Content-Type', 'text/xml;;charset=utf-8')
        if ob.meta_type == 'Blob File' :
            return '<element id="%s" title="%s"/>' % (ob.getId(), escape(ob.title_or_id()))
        elif ob.meta_type == 'Photo' :
            width, height = ob.getResizedImageSize(size=(310, 310))
            return '<element src="%(src)s" title="%(title)s" width="%(width)d" height="%(height)d"/>' % \
                {'src' : 'attachments/%s/getResizedImage?size=%d_%d' % (ob.getId(), width, height),
                 'title' : escape(ob.title_or_id()),
                 'width' : width,
                 'height' : height
                 }
    
    security.declareProtected(ModifyPortalContent, 'removeUnusedAttachments')
    def removeUnusedAttachments(self, html) :
        html = '<div>%s</div>' % html
        doc = htmlReadDoc(html, '', None, PARSE_OPTIONS)
        used = {}

        hrefs = doc.xpathEval('//a/@href')
        for href in [a.get_content() for a in hrefs] :
            if href.startswith('attachments/') :
                used[href[len('attachments/'):]] = True

        srcs = doc.xpathEval('//img/@src')
        for src in [a.get_content() for a in srcs] :
            if src.startswith('attachments/') :
                parts = src.split('/')
                if len(parts) >=2 :
                    used[parts[1]] = True
        
        unused = [id for id in self.objectIds() if not used.has_key(id)]
        if unused :
            self.manage_delObjects(unused)

InitializeClass(AttachmentContainer)