"""Definition of the Content Panels content type.""" ############################################################################## # # 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 # ############################################################################## import pprint from copy import deepcopy from zope.interface import implements from AccessControl import ClassSecurityInfo from Acquisition import aq_base, aq_parent, aq_inner from Products.CMFCore import permissions from Products.ATContentTypes.content import base from Products.ATContentTypes.content import schemata from Products.CMFContentPanels import MessageFactory as _ from Products.CMFContentPanels.config import PLONE_VERSION from Products.CMFContentPanels.config import VOC_PAGE_LAYOUT, PROJECTNAME from Products.CMFContentPanels.interfaces import IContentPanels try: from Products.LinguaPlone.public import registerType, BaseContent from Products.LinguaPlone.public import Schema from Products.LinguaPlone.public import StringField from Products.LinguaPlone.public import ReadOnlyStorage from Products.LinguaPlone.public import TextAreaWidget, SelectionWidget except ImportError: from Products.Archetypes.atapi import registerType, BaseContent from Products.Archetypes.atapi import Schema from Products.Archetypes.atapi import StringField from Products.Archetypes.atapi import ReadOnlyStorage from Products.Archetypes.atapi import TextAreaWidget, SelectionWidget ContentPanelsSchema = schemata.ATContentTypeSchema.copy() + Schema(( StringField( name='description', default='', searchable=True, accessor='Description', widget=TextAreaWidget( label_msgid='label_description', description_msgid='help_description', i18n_domain='plone', ), ), StringField( name='panelsConfig', edit_accessor='getPanelsConfig', widget=TextAreaWidget( visible={'edit': 'invisible', 'view': 'invisible'}, ), ), StringField( name='pageLayoutMode', default='tile', vocabulary=VOC_PAGE_LAYOUT, widget=SelectionWidget( label=_(u'label_page_layout_mode', u'Page layout mode'), #label_msgid='label_page_layout_mode', description=_(u"help_page_layout_mode", u"You can choose 'tile mode' or 'tab mode'. With" u"'tile mode', all pages are shown directly as rows. " u"It is useful for you to make very complex composite " u"page. With 'tab mode', you can switch pages using " u"the top-right tab links."), #description_msgid="help_page_layout_mode", ), ), StringField( name='customCSS', widget=TextAreaWidget( label=_(u"label_custom_css", u'Custom CSS'), #label_msgid='label_custom_css', description=_(u"help_custom_css", u"You can define custom CSS for this contentpanels " u"here. Leave it blank if you don't know about CSS."), #description_msgid='help_custom_css', i18n_domain='contentpanels', ), ), )) schemata.finalizeATCTSchema(ContentPanelsSchema, moveDiscussion=False) class ContentPanels(base.ATCTContent): """ContentPanels is a composite page build system.""" implements(IContentPanels) schema = ContentPanelsSchema archetype_name = 'ContentPanels' meta_type = 'CMF Content Panels' security = ClassSecurityInfo() _at_rename_after_creation = True def __init__(self, oid, **kw): BaseContent.__init__(self, oid, **kw) self.clearPanels() self.addPage() security.declarePublic('ofContext') def ofContext(self, context): """ used when used as a template, return a contentpanels under the context of the new object """ # create a new object to avoid any security problem new_cp = ContentPanels('tmp') new_cp.panelsConfig = deepcopy(self.panelsConfig) new_cp.pageLayoutMode = self.pageLayoutMode new_cp.customCSS = self.customCSS return new_cp.__of__(context) def getPanelsConfig(self): return pprint.pformat(self.panelsConfig) def setPanelsConfig(self, value, **kw): if not value.strip(): return value = value.replace('\n', ' ').replace('\r', '') self.panelsConfig = eval(value) 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) folderContext = 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:] # folderContext = aq_inner(self) folderContext = self if not self.isPrincipiaFolderish: folderContext = aq_parent(folderContext) 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=None, 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 pageTitle is None: pageTitle = _(u'Untitled page') if pageIndex == -1: pageIndex = len(self.panelsConfig) self.panelsConfig.insert(pageIndex, {'pageColumns': [], 'pageTitle': pageTitle, 'pageWidth':'100%', 'pageCellSpace':'4', 'pageCellPad':'4', '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': '50%', '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 def getText(self): """ return the whole ContentPanels content, one panel after another, to add support for collective.portlet.content. """ return self.restrictedTraverse('contentpanels_body').render() registerType(ContentPanels, PROJECTNAME)