##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

import Globals, string
from ForumPermissions import AddForumPosting
from DateTime import DateTime
from OFS.Image import File, Image
from OFS.content_types import guess_content_type
from AccessControl import ClassSecurityInfo
from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
from Products.CMFCore.utils import getToolByName
from Products.CMFDefault.Document import Document
from Products.CMFDefault.SkinnedFolder import SkinnedFolder
from Products.CMFCore.CMFCorePermissions import View, ManageProperties, \
     ListFolderContents, ModifyPortalContent, AddPortalContent
from Products.CMFForum.ForumPermissions import AddForumPosting, ViewForum
from interfaces.Post import IPost

factory_type_information =   { 'id'             : 'Post'
                             , 'portal_type'    : 'Post'
                             , 'meta_type'      : 'Post'
                             , 'description'    : """\
Posts are like Discussion items but they just can be added into Forums."""
                             , 'icon'           : 'icon_post.gif'
                             , 'product'        : 'CMFForum'
                            , 'factory'        : 'addPost'
                             , 'filter_content_types' : 1
                             , 'allowed_content_types' : ('Post', 'File', 'Image' ) 
                             , 'immediate_view' : 'metadata_edit_form'
                             , 'global_allow'   : 0                               
                             , 'actions'        :
                                ( { 'id'            : 'view' 
                                  , 'name'          : 'View'
                                  , 'action'        : 'post_view'
                                  , 'permissions'   : (
                                      View, )
                                  }
                                , { 'id'            : 'post_reply'
                                  , 'name'          : 'Reply'
                                  , 'action'        : 'post_post_message'
                                  , 'permissions'   : ('Add Forum posting', )
                                  }
                                , { 'id'            : 'post_post'
                                  , 'name'          : 'Post'
                                  , 'action'        : 'forum_post_message'
                                  , 'permissions'   : ('Add Forum posting', )
                                  }
                                , { 'id'            : 'forum_home'
                                  , 'name'          : 'Forum Home'
                                  , 'action'        : 'forum_post_toForum'
                                  , 'permissions'   : (View, )
                                  }
                                , { 'id'            : 'forum_search'
                                  , 'name'          : 'Search Forum'
                                  , 'action'        : 'forum_post_toSearch'
                                  , 'permissions'   : (View, )
                                  }
                                )
                             }

def generate_id(self):
    """Find an available ID"""
    id = DateTime().millis()
    str_id = 'msg_%s'
    while hasattr(self, str_id % id):
        id = DateTime().millis()
        
    return str_id % id

class Post(SkinnedFolder, Document):

    meta_type = 'Post'
    __implements__= (IPost, )
    manage_options = SkinnedFolder.manage_options
    security = ClassSecurityInfo()


    def __init__(self, id, title='', description='', text='', author='', email='', in_reply_to='', text_format='plain'):
        DefaultDublinCoreImpl.__init__(self)
        self.id = id
        self.title = title
        self.description = description
	self.author = author
	self.email = email
        self._edit( text=text, text_format=text_format )
        self.setFormat( text_format )
	#self.created = self.modified = DateTime()
	# DefaultDublinCoreImpl.Date() uses now expects modified to be used,
	# differently
	self.created = DateTime()

    security.declareProtected(ViewForum, 'thread_top')
    def thread_top(self):
        """ returns the top ancestor """
        if len(self.ancestors()) > 0:
                return self.ancestors()[-1]
        else:
                return self

    security.declareProtected(ViewForum, 'ancestors')
    def ancestors(self):
	"""Return a list of ancestral posts"""
	ancestors = []
	parent = self.aq_inner.aq_parent
	while parent.meta_type=='Post':
            ancestors.append(parent)
            parent = parent.aq_inner.aq_parent
	return ancestors

    security.declareProtected(ViewForum, 'children')
    def children(self, sort_key='id', REQUEST=None):
	"""Return a list of replies to this item"""
	if REQUEST is None:
	    REQUEST = self.REQUEST
	children = []

        for child in self.contentValues(['Post']):
            children.append(child)
        children.sort(lambda x, y, sort_key=sort_key: cmp(getattr(x, sort_key, None) , getattr(y, sort_key, None)))
	return children

    security.declareProtected(ViewForum, 'inReplyTo')
    def inReplyTo(self):
        """
        Return the object for which this object is a reply.
        """
        parent = self.aq_inner.aq_parent
        if parent.meta_type=='Post':
            return parent
        else:
            return None

    # XXX remove this, use nextReplies
    security.declareProtected(ViewForum, 'nextReply')
    def nextReply(self):
        """
        Return the next object that is reply.
        """
        ids = self.contentIds(['Post'])
        ids.sort()
        if ids:
            return getattr(self, ids[0], None)
        return None

    security.declareProtected(ViewForum, 'nextReplies')
    def nextReplies(self):
        """
        Return a sequence of objects that are replies.
        """
        ids = self.contentIds(['Post'])
        ids.sort()
        if ids:
            return map(lambda x,self=self:getattr(self, x), ids)
        return None

    # XXX remove this, use nextMessages
    security.declareProtected(ViewForum, 'nextMessage')
    def nextMessage(self):
        """
        Return the next object on the same level.
        """
        ids = self.aq_inner.aq_parent.contentIds(['Post'])
        ids.sort()
        index = ids.index(self.getId())
        next = index + 1
        if next < len(ids):
            return getattr(self, ids[next], None)
        return None

    security.declareProtected(ViewForum, 'nextMessages')
    def nextMessages(self):
        """
        Return a sequence of next objects on the same level.
        """
        ids = self.aq_inner.aq_parent.contentIds(['Post'])
        ids.sort()
        index = ids.index(self.getId())
        next = index + 1
        if next < len(ids):
            return map(lambda x,self=self:getattr(self, x), ids[next:])
        return None

    # XXX remove this, use previousMessages
    security.declareProtected(ViewForum, 'previousMessage')
    def previousMessage(self):
        """
        Return the previous object on the same level.
        """
        ids = self.aq_inner.aq_parent.contentIds(['Post'])
        ids.sort()
        index = ids.index(self.getId())
        previous = index - 1
        if previous >= 0:
            return getattr(self, ids[previous], None)
        return None

    security.declareProtected(ViewForum, 'previousMessages')
    def previousMessages(self):
        """
        Return a sequence of previous objects on the same level.
        """
        ids = self.aq_inner.aq_parent.contentIds(['Post'])
        ids.sort()
        ids.reverse()
        index = ids.index(self.getId())
        previous = index + 1
        if previous >= 0:
            return map(lambda x,self=self:getattr(self, x), ids[previous:])
        return None

    security.declareProtected(ViewForum, 'in_reply_to')
    def in_reply_to(self):
        """
        Return the id of the object for which this object is a reply.
        """
        parent = self.aq_inner.aq_parent
        if parent.meta_type=='Post':
            return parent.getId()
        else:
            return ''

    security.declareProtected(ViewForum, 'childMessageCount')
    def childMessageCount(self):
        """ Counts the number of chilren of this message, including this message"""
        count=1 # this message
        for message in self.children():
                count = count + message.childMessageCount()
        return count

    security.declareProtected(ViewForum, 'threadMessageCount')
    def threadMessageCount(self):
        """ Looks at the topmost ancestor, and then counts children"""

        message_count = self.thread_top().childMessageCount()
        return message_count

    security.declareProtected(ViewForum, 'childNewestMessageDate')
    def childNewestMessageDate(self):
        """ Looks through children and returns the newest message date"""
        newest=self.created
        for message in self.children():
                test_date = message.childNewestMessageDate()
                if test_date > newest:
                       newest = test_date
        return newest

    security.declareProtected(ViewForum, 'threadNewestMessageDate')
    def threadNewestMessageDate(self):
        """ Looks at the topmost ancestor, and then recurses
            through the messages finding the last created message"""
        newest=self.thread_top().childNewestMessageDate()
        return newest

    security.declareProtected(ViewForum, 'hasAttachments')
    def hasAttachments(self):
        """
        Boolean value to test if the message has any attachment.
        """
        return self.contentIds(['Portal File', 'Portal Image'])

    security.declareProtected(ViewForum, 'getAttachments')
    def getAttachments(self):
        """
        Return a list of attachment objects.
        """
        return self.contentValues(['Portal File', 'Portal Image'])

    security.declareProtected(AddForumPosting, 'addPost')
    def addPost(self, id='', title='', text='', author='', email='',
                file=None, REQUEST=None, text_format='structured-text'):
        """Add a Post to its container"""

        if not id:
            id = generate_id(self)
        item = Post(id, title=title, text=text, author=author, email=email,
                    text_format=text_format)
        self._setObject(id, item)
        post = getattr(self, id)
        post._setPortalTypeName('Post')

        if file and file.filename:
	    id = string.split(file.filename, "\\")[-1]
            body = file.read()
            content_type = file.headers['Content-Type']
            if guess_content_type(id, body)[0][0:5] == 'image':
                post.invokeFactory( 'Image', id=id, file=file,
                                    content_type=content_type)
            else:
                post.invokeFactory( 'File', id=id, file=file,
                                    content_type=content_type)

        if REQUEST is not None:
            return self.manage_main(self, REQUEST)

addPost = Post.addPost

Globals.InitializeClass(Post)
