##############################################################################
#
# Copyright (c) 2002 ZopeChina Corporation (http://www.zopechina.com). 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
#
##############################################################################

"""Implement the content panels content type."""
import pprint
from AccessControl import ClassSecurityInfo
from Acquisition import aq_base, aq_parent, aq_inner
from Products.CMFCore import permissions
from Products.Archetypes.public import registerType, BaseContent
from Products.Archetypes.public import BaseSchema, Schema
from Products.Archetypes.public import StringField
from Products.Archetypes.public import ReadOnlyStorage
from Products.Archetypes.public import TextAreaWidget, SelectionWidget
from config import VOC_PAGE_LAYOUT, VOC_PORTLET_POS, PROJECTNAME

ContentPanelsSchema = BaseSchema + Schema((

    StringField('panelsConfig',
#                schemata="debug", 
                edit_accessor = 'getPanelsConfig',
                widget=TextAreaWidget(description="""debug only. don't touch this if you don't
                know detail about it.""",
                                      visible = {'edit':'invisible', 'view':'invisible'},
                                      size=20)
                ),
    StringField('pageLayoutMode',
                default="tab",
                vocabulary=VOC_PAGE_LAYOUT,
                widget=SelectionWidget(i18n_domain="contentpanels",
                                   label="page layout mode",
                                   label_msgid="label_page_layout_mode",
                                   description="""You can choose 'tile mode' or
          'tab mode'. With 'tile mode', all pages are shown directly as rows.
          It is useful for you to make very complex composite page.
          With 'tab mode', you can switch pages using the top-right tab
         links.""",
                                   description_msgid="help_page_layout_mode",)
                ),
    StringField('portletsPos',
                vocabulary=VOC_PORTLET_POS,
                accessor = 'getPortletsPos',
                edit_accessor = 'getPortletsPos',
                mutator='setPortletsPos',
                storage = ReadOnlyStorage(),
                widget=SelectionWidget(i18n_domain="contentpanels",
                          label="set to left/right column",
                          label_msgid="label_portlet_pos",
                          description="""You can set this contentpanels as the
                          left or right column of the template.""",
                          description_msgid="help_portlet_pos",)
               ),
    StringField('customCSS',
                widget=TextAreaWidget(i18n_domain="contentpanels",
                           label="Custom CSS",
                           label_msgid="label_custom_css",
                           description_msgid="help_custom_css",
                           description="""You can define custom CSS for this
                           contentpanels here. Leave it blank if you don't know
                           about CSS.""")),

    ))

class ContentPanels(BaseContent):
    """
    content panels class.
    """

    schema = ContentPanelsSchema

    __implements__ = (getattr(BaseContent,'__implements__',()),)

    security = ClassSecurityInfo()
    archetype_name             = 'ContentPanels'
    meta_type                  = 'CMF Content Panels'
    portal_type                = 'ContentPanels'
    allowed_content_types      = []
    filter_content_types       = 0
    global_allow               = 1
    allow_discussion           = 0
    content_icon               = 'contentpanels_icon.gif'
    immediate_view             = 'contentpanels_edit_form'
    default_view               = 'contentpanels_view'
    suppl_views                = ()

    typeDescription            = "ContentPanels is a portlet content to build composite page."
    typeDescMsgId              = 'description_edit_contentpanels'

    _at_rename_after_creation = True

    actions = (
               { 'id': 'view',
                 'name': 'View',
                 'action': 'string:${object_url}/contentpanels_view',
                 'permissions': (permissions.View,)
                }
             , {'id': 'layout',
                 'name': 'Layout',
                 'action': 'string:${object_url}/contentpanels_config_form',
                 'permissions': (permissions.ModifyPortalContent,)
               }
             , {'id':'local_roles',
                'name':'Sharing',
                'action':'string:${object_url}/folder_localrole_form',
                'permissions': ('Manage properties',)
               }
            )

    # XXX remove this
    _panelsConfig = [ {'pageColumns': [
                                    {'columnWidth': '0',
                                     'columnPanels':[] },
                                    {'columnWidth': '0',
                                     'columnPanels':[] },
                                   ],
                         'pageTitle': 'untitled page',
                         'pageWidth':'100%',
                         'pageCellSpace':'0',
                         'pageCellPad':'3',
                         'pageAlign':'center',
                         'pageStylesheetFixed':[],
                         'pageStylesheetDynamic':[]}
                    ]

    def __init__(self, oid, **kw):
        BaseContent.__init__(self, oid, **kw)
        self.clearPanels()
        self.addPage()

    def getPanelsConfig(self):
        return pprint.pformat(self.panelsConfig)

    def setPanelsConfig(self, value):
        if not value.strip():
            return
        value = value.replace('\n', ' ').replace('\r', '')
        self.panelsConfig = eval(value)
        
    def getPortletsPos(self):
        """ get portlet pos of the container """
        portlet_name = 'here/%s/contentpanels_body' % self.getId()
        container = aq_parent(self)
        for slot_name in ['left_slots', 'right_slots']:
            if hasattr(aq_base(container), slot_name):
                if portlet_name in getattr(container, slot_name):
                    return slot_name
        return 'none'

    def setPortletsPos(self, value):
        self.cleanPortletPos()
        if value == 'none':
            return

        if value not in ('left_slots', 'right_slots'):
            return

        portlet_path = 'here/%s/contentpanels_body' % self.getId()

        folder = aq_parent(aq_inner(self))
        if not folder.hasProperty(value):
            folder.manage_addProperty(value, [portlet_path], 'lines')
        else:
            portlets = folder.getProperty(value)
            new_portlets = list(portlets) + [portlet_path]
            folder.manage_changeProperties({value:new_portlets})

    def cleanPortletPos(self):
        """ cleanup left/right_slots"""
        portlet_path = 'here/%s/contentpanels_body' % self.getId()
        folder = aq_parent(aq_inner(self))
        for slot_name in ('left_slots', 'right_slots'):
            portlets = folder.getProperty(slot_name, ())

            if portlet_path not in portlets:
                continue

            if tuple(portlets) == (portlet_path, ):
                folder.manage_delProperties([slot_name])
            else:
                new_portlets = [portlet for portlet in portlets \
                                if portlet != portlet_path]
                folder.manage_changeProperties({slot_name:new_portlets})

    def manage_beforeDelete(self, item, container):
        """ cleanup left/right slots when delete or rename """
        # delete myself
        if item is self:
            self.cleanPortletPos()
        BaseContent.manage_beforeDelete(self, item, container)

    def clearPanels(self):
        self.panelsConfig = []
        self._p_changed = 1

    security.declarePublic('toRelativePath')
    def toRelativePath(self, panelObjectPath):
        """ regenerate panelObjectPath, make it a relative path.
        path may be relative to this contentpanels or relate to the portal.
        - if panelObjectPath == '.', it means contentpanels it self
        - if panelObjectPath start with './', it means relative to the folderish context of this contentpanels
        - else, it means rlative to portal
        see also: getPanelObject
        """
        panelContent = self.getPanelObject(panelObjectPath)
        if panelContent is None: 
            return '.'

        folderContext = aq_inner(self)
        if not folderContext.isPrincipiaFolderish:
            folderContext = aq_parent(folderContext)

        relativePath = self.portal_url.getRelativeContentURL(panelContent)
        if panelContent is self:
            return '.'
        else:
            folderContextPath = self.portal_url.getRelativeContentURL(folderContext)
            if relativePath.startswith(folderContextPath):
                relativePath = panelObjectPath[len(folderContextPath):]
                relativePath = (relativePath.startswith('/') and  '.' or './')\
                               + relativePath
        return relativePath

    def getPublishContext(self):
        """ We want to display portlet in current context when the contenpanels
        is in left/right slots """
        o = self.REQUEST.get('PUBLISHED', None) # Gets the current object bound to REQUEST
        if hasattr(o, 'im_self'):       # It's a class method
            contextObject = o.im_self     # Gets the class instance bound to this method
            method = o
        elif hasattr(o, '_isPortalContent') or hasattr(o, '_getPortalTypeName'): # It's a CMF Content
            contextObject = o             # Gets the class instance itself
            method = None
        else:                           # It's a python script or ZPT
            contextObject = aq_parent(o)  # Gets parent's context
            method = o
        return (contextObject, method)

    def getPanelObject(self, objectPath):
        """get panel object by path.

        if panelObjectPath == '.', it means contentpanels it self
        if panelObejctPath start with './', it means relative to folderish context of this contentpanels
        else, it means rlative to the portal
        see also: toRelativePath
        """
        panelObject = None
        try:
            if objectPath in ['.', '/']:  # '.'means the contentpanels it self
                panelObject = self
            elif objectPath.startswith('./'):  # relative path to the folderish context
                objectPath = objectPath[2:]

                if not self.isPrincipiaFolderish:
                    folderContext = aq_parent(aq_inner(self))
                else:
                    folderContext = aq_inner(self)

                panelObject = folderContext.restrictedTraverse(objectPath)
            else:
                panelObject = self.portal_url.getPortalObject().restrictedTraverse(objectPath)
        except:
            panelObject = None
        return panelObject

    security.declareProtected( permissions.ModifyPortalContent, 'addPage' )
    def addPage(self, pageTitle='Untitled page', pageIndex=-1):
        """
        add a new page at pageIndex, it has two columns as default.
        if pageIndex is -1 then add at the end of the contentpanels
        return the new page index (from 0)
        """
        if pageIndex == -1:
            pageIndex = len(self.panelsConfig)
        self.panelsConfig.insert(pageIndex, {'pageColumns': [],
                         'pageTitle': pageTitle,
                         'pageWidth':'100%',
                         'pageCellSpace':'0',
                         'pageCellPad':'3',
                         'pageAlign':'center',
                         'pageStylesheetFixed':[],
                         'pageStylesheetDynamic':[]})

        # add two default columns for the new page
        self.addColumn(pageIndex)
        self.addColumn(pageIndex)
        self._p_changed = 1
        return pageIndex

    security.declareProtected( permissions.View, 'getPageTitles' )
    def getPageTitles(self):
        ''' get all the tilte of the pages '''
        titles = []
        for pageIndex in range(len(self.panelsConfig) ):
            titles.append(self.panelsConfig[pageIndex]['pageTitle'])
        return titles

    security.declareProtected( permissions.View, 'getPageInfo' )
    def getPageInfo(self, pageIndex, infoName):
        ''' get general info of the page '''
        return self.panelsConfig[pageIndex][infoName]

    security.declareProtected( permissions.ModifyPortalContent, 'changePageInfo' )
    def changePageInfo(self, pageIndex, pageTitle="", pageCellPad='', pageCellSpace='', pageWidth='', pageAlign=''):
        ''' change page's table info '''
        if pageCellPad == '':
            pageCellPad = None
        if pageCellSpace == '':
            pageCellSpace = None
        if pageWidth == '':
            pageWidth = None
        if pageAlign == '' or pageAlign == 'noalign':
            pageAlign = None

        self.panelsConfig[pageIndex]['pageWidth']= pageWidth
        self.panelsConfig[pageIndex]['pageAlign']= pageAlign
        self.panelsConfig[pageIndex]['pageCellPad']= pageCellPad
        self.panelsConfig[pageIndex]['pageCellSpace']= pageCellSpace
        self.panelsConfig[pageIndex]['pageTitle']= pageTitle
        self._p_changed = 1

    security.declareProtected(permissions.ModifyPortalContent, 'movePage')
    def movePage(self, pageIndex, toPage):
        """move a page from fromIndex to toIndex"""
        page = self.panelsConfig.pop(pageIndex)
        self.panelsConfig.insert(toPage, page)
        self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'deletePage' )
    def deletePage(self, pageIndex):
        ''' delete a page,
        return next page index to show'''
        nextPageIndex = pageIndex
        if len(self.panelsConfig) > 1:  # can't delete the last page!
            del self.panelsConfig[pageIndex]

            if pageIndex == len(self.panelsConfig):
                nextPageIndex = pageIndex - 1
        self._p_changed = 1
        return nextPageIndex

    security.declareProtected( permissions.ModifyPortalContent, 'addColumn' )
    def addColumn(self, pageIndex, columnIndex=-1):
        """add a new Column to 'pageIndex' at 'columnIndex'
        if 'columnIndex' is -1 then add to the end of the column'
        """
        if columnIndex == -1:
           columnIndex = len(self.panelsConfig[pageIndex]['pageColumns'])

        self.panelsConfig[pageIndex]['pageColumns'].insert(columnIndex, {'columnWidth': '0',
                                                            'columnPanels':[] })
        self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'changeColumnWidth' )
    def changeColumnWidth(self, pageIndex, columnIndex, columnWidth):
        ''' change the width of a column '''
        if columnWidth == '':
            return None

        self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnWidth'] = columnWidth
        self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'moveColumn')
    def moveColumn(self, pageIndex, columnIndex, toColumn):
        """move a column from 'fromIndex' to 'toIndex'"""
        column = self.panelsConfig[pageIndex]['pageColumns'].pop(columnIndex)
        self.panelsConfig[pageIndex]['pageColumns'].insert(toColumn, column)
        self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'deleteColumn' )
    def deleteColumn(self, pageIndex, columnIndex):
        '''# delete column'''
        if len(self.panelsConfig[pageIndex]['pageColumns']) > 1:
            del self.panelsConfig[pageIndex]['pageColumns'][columnIndex]
            self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'addPanel' )
    def addPanel(self, pageIndex, columnIndex, panelIndex, panelObjectPath, panelObjectViewlet, panelSkin, viewletOptions):
        ''' insert a new panel at panelIndex'''
        self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnPanels'].\
                insert(panelIndex, {'panelSkin':panelSkin,
                        'panelObjectPath':panelObjectPath,
                        'panelObjectViewlet':panelObjectViewlet,
                        'viewletOptions':viewletOptions})
        self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'deletePanel' )
    def deletePanel(self, pageIndex, columnIndex, panelIndex):
        ''' delete a Panel '''
        del self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnPanels'][panelIndex]
        self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'changePanel' )
    def changePanel(self, pageIndex, columnIndex, panelIndex, panelObjectPath='', panelObjectViewlet='', panelSkin='', viewletOptions=None):
        ''' change the skin of a existing panel '''
        
        if panelSkin:
            self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnPanels'][panelIndex]['panelSkin'] = panelSkin
        self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnPanels'][panelIndex]['panelObjectPath'] = panelObjectPath
        if panelObjectViewlet:
            self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnPanels'][panelIndex]['panelObjectViewlet'] = panelObjectViewlet
        if viewletOptions is not None:
            self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnPanels'][panelIndex]['viewletOptions']=viewletOptions
        self._p_changed = 1

    security.declareProtected( permissions.ModifyPortalContent, 'movePanel')
    def movePanel(self, pageIndex, columnIndex, panelIndex, toColumn, toPanel):
        """move a panel from 'toIndex'"""
        if toColumn == -1: 
            toColumn = columnIndex
        if toPanel == -1: 
            toPanel = panelIndex

        panel = self.panelsConfig[pageIndex]['pageColumns'][columnIndex]['columnPanels'].pop(panelIndex)
        toColumnLen = len(self.panelsConfig[pageIndex]['pageColumns'][toColumn]['columnPanels'])
        if toPanel > toColumnLen:
            toPanel = toColumnLen
        self.panelsConfig[pageIndex]['pageColumns'][toColumn]['columnPanels'].insert(toPanel, panel)
        self._p_changed = 1

registerType(ContentPanels, PROJECTNAME)

