From: Benoît Pin Date: Mon, 25 Oct 2010 11:20:06 +0000 (+0200) Subject: Copie de MosaicDocument à partir de : X-Git-Url: https://scm.cri.mines-paristech.fr/git/MosaicDocument.git/commitdiff_plain/1aed5a5073041e18e085d5f03b320efa7e9a0f71?ds=inline Copie de MosaicDocument à partir de : URL: http://svn.cri.ensmp.fr/svn/MosaicDocument/branches/CMF-2.1 Repository Root: http://svn.cri.ensmp.fr/svn/MosaicDocument Repository UUID: 1bf790b2-e4d4-0310-9a6c-c8a412c25dae Revision: 648 Node Kind: directory Schedule: normal Last Changed Author: pin Last Changed Rev: 648 Last Changed Date: 2008-11-29 12:18:49 +0100 (Sam, 29 nov 2008) --- 1aed5a5073041e18e085d5f03b320efa7e9a0f71 diff --git a/BaseSlot.py b/BaseSlot.py new file mode 100755 index 0000000..e9d19ea --- /dev/null +++ b/BaseSlot.py @@ -0,0 +1,100 @@ +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from Globals import InitializeClass, DTMLFile +from Products.CMFCore.utils import getToolByName + +from AccessControl import ClassSecurityInfo +from Products.CMFCore.permissions import View, ModifyPortalContent +from Products.CMFCore.DynamicType import DynamicType +from Products.PageTemplates.Expressions import getEngine +from Products.PageTemplates.Expressions import SecureModuleImporter + +class BaseSlot(DynamicType) : + """Slot""" + + _isMosaicSlot = 1 + _editableFields = [] + _indexableFields = [] + + security = ClassSecurityInfo() + + security.declarePublic('callAction') + def callAction(self, actionId, *args, **kw) : + """call action from action definitions""" + typeTool = getToolByName(self, 'portal_types') + + # !!! id et meta_type doivent etre identiques dans la fti. + typeInfo = typeTool.getTypeInfo(self) + actionInfo = typeInfo.getActionInfo('object/%s' % actionId) + zpt = getattr(self, actionInfo['url']) + return zpt(object=self, block=self.aq_parent, *args, **kw) + + security.declareProtected(ModifyPortalContent, 'edit') + def edit(self, **kw) : + """ Edit Slot""" + for fieldName in self._editableFields : + if kw.has_key(fieldName) : + setattr(self, fieldName, kw[fieldName]) + + security.declareProtected(View, 'getBlock') + def getBlock(self) : + """ Return the block object where the slot is located """ + return self.aq_parent + + security.declareProtected(View, 'getExprContext') + def getExprContext(self, REQUEST=None) : + """Return an expression context customized for expressions properties from slot information""" + block = self.aq_parent + while block.meta_type != 'Mosaic Block' : + block = block.aq_parent + data = { + 'portal' : self.portal_url.getPortalObject(), + 'slot' : self, + 'block' : self.aq_parent, + 'here' : None, + 'request' : REQUEST, + 'modules' : SecureModuleImporter, + 'nothing' : None, + } + return getEngine().getContext(data) + + security.declareProtected(View, 'SearchableText') + def SearchableText(self) : + """ Return full text for indexation """ + text = '' + for fieldName in self._indexableFields : + field = getattr(self, fieldName) + if callable(field) : + text += ' %s' % field() + else : + text += ' %s' % field + + return text + + def _initDefaultValues(self) : + pass + + def indexObject(self): pass + def unindexObject(self): pass + def reindexObject(self, idxs=[]): pass + def reindexObjectSecurity(self): pass + def notifyWorkflowCreated(self): pass + + +InitializeClass(BaseSlot) diff --git a/FileSlot.py b/FileSlot.py new file mode 100755 index 0000000..4b7c42e --- /dev/null +++ b/FileSlot.py @@ -0,0 +1,84 @@ +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from OFS.SimpleItem import SimpleItem +from OFS.PropertyManager import PropertyManager +from OFS.Image import cookId +from Globals import InitializeClass +from Products.CMFCore.utils import getToolByName + +from Products.CMFCore.permissions import View, ModifyPortalContent +from AccessControl import ClassSecurityInfo +from Products.Plinn.File import File +from BaseSlot import BaseSlot + +factory_type_information = ( {'id' : 'File Slot', + 'meta_type' : 'File Slot', + 'description' : "File Slot for Mosaic Document", + 'icon' : 'mosaic_tool/bin_icon.gif', + 'product' : 'MosaicDocument', + 'factory' : 'addFileSlot', + 'immediate_view' : 'view', + 'actions' : + ({'id' : 'view', + 'name' : 'View', + 'action' : 'slot_file_view', + 'permissions' : (View, ) + }, + + {'id' : 'edit', + 'name' : 'Edit', + 'action' : 'slot_file_form', + 'permissions' : (ModifyPortalContent, ) + }, + ) + }, + ) + +class FileSlot(BaseSlot, File) : + """File Slot""" + meta_type = 'File Slot' + + filename = '' + _indexableFields = ('title',) + + security = ClassSecurityInfo() + + security.declareProtected(ModifyPortalContent, 'edit') + def edit(self, title='', file='') : + """ Edit file slot """ + self.title = title + if file : + self._edit(file = file) + self.filename = cookId(None, None, file)[1] + + + security.declareProtected(View, 'SearchableText') + def SearchableText(self) : + """ Return full text for indexation """ + return BaseSlot.SearchableText(self) + File.SearchableText(self) + + +InitializeClass(FileSlot) + +def addFileSlot(dispatcher, id, title='', file=''): + """ + Add a new FileSlot object. + """ + o = FileSlot(id, title=title, file=file) + dispatcher.Destination()._setObject(id, o) diff --git a/ImageSlot.py b/ImageSlot.py new file mode 100755 index 0000000..2563fbc --- /dev/null +++ b/ImageSlot.py @@ -0,0 +1,119 @@ +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from OFS.Image import cookId +from Globals import InitializeClass + +from Products.CMFCore.permissions import View, ModifyPortalContent +from AccessControl import ClassSecurityInfo +from Products.Photo import Photo +from BaseSlot import BaseSlot + +factory_type_information = ( {'id' : 'Image Slot', + 'meta_type' : 'Image Slot', + 'description' : "Image Slot for Mosaic Document", + 'icon' : 'mosaic_tool/photo_icon.gif', + 'product' : 'MosaicDocument', + 'factory' : 'addImageSlot', + 'immediate_view' : 'view', + 'actions' : + ({'id' : 'view', + 'name' : 'View', + 'action' : 'slot_image_view', + 'permissions' : (View, ) + }, + + {'id' : 'edit', + 'name' : 'Edit', + 'action' : 'slot_image_form', + 'permissions' : (ModifyPortalContent, ) + }, + ) + }, + ) + +class ImageSlot(BaseSlot, Photo) : + """Slot""" + meta_type = 'Image Slot' + manage_options = Photo.manage_options + + _indexableFields = ('title',) + + security = ClassSecurityInfo() + + + def __init__(self, parentContainer, id, title='', file='', + content_type='', precondition='', blankThumbnail = '', **kw) : + blankThumbnailOb = None + if blankThumbnail : + blankThumbnailOb = parentContainer.restrictedTraverse(blankThumbnail) + Photo.__init__(self, id, title=title, file=file, + defaultBlankThumbnail = blankThumbnailOb, **kw) + + + # This method is overloaded to manage file upload. + # It's necessary to use manage_upload method from image + # because manage_upload invoke update_data and the overload of + # update_data in Photo product update the internal thumbnail image. + security.declareProtected(ModifyPortalContent, 'edit') + def edit(self, title='', thumb_width='440', thumb_height='440', file='', REQUEST=None): + """ Edit image slot""" + if file : + self.manage_upload(file=file) + + if self.thumb_height != thumb_height or thumb_width != thumb_width : + if thumb_width <= thumb_height : + self.manage_editProperties(thumb_width=thumb_width, thumb_height=thumb_height) + else : + self.manage_editProperties(thumb_width=thumb_height, thumb_height=thumb_width) + if title : + self.manage_editProperties(title = title, no_refresh = 1) + + view = index_html = __call__ = Photo.index_html + +InitializeClass(ImageSlot) + +def addImageSlot(dispatcher, id, file='', title='', + precondition='', content_type='', REQUEST=None, **kw) : + """ + Add a new Photo object. + Creates a new Photo object 'id' with the contents of 'file'. + """ + id=str(id) + title=str(title) + content_type=str(content_type) + precondition=str(precondition) + + id, title = cookId(id, title, file) + parentContainer = dispatcher.Destination() + + # First, we create the image without data: + parentContainer._setObject(id, ImageSlot(parentContainer, id,title,'',content_type, precondition, **kw)) + + # Now we "upload" the data. By doing this in two steps, we + # can use a database trick to make the upload more efficient. + if file: + parentContainer._getOb(id).manage_upload(file) + if content_type: + parentContainer._getOb(id).content_type=content_type + + if REQUEST is not None: + try: url=dispatcher.DestinationURL() + except: url=REQUEST['URL1'] + REQUEST.RESPONSE.redirect('%s/manage_main' % url) + return id diff --git a/MosaicBlock.py b/MosaicBlock.py new file mode 100755 index 0000000..2904cbf --- /dev/null +++ b/MosaicBlock.py @@ -0,0 +1,546 @@ +# -*- coding: utf-8 -*- +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + + +from Products.CMFCore.PortalFolder import PortalFolder +from Globals import InitializeClass +from AccessControl import ClassSecurityInfo +from AccessControl.Permission import Permission +from Products.CMFCore.utils import getToolByName +from Products.CMFCore.permissions import View, ModifyPortalContent, AccessContentsInformation +from random import randrange +from DateTime import DateTime +from types import InstanceType, StringType, DictType +from MosaicBlockInformation import RuleError + +from OFS.Moniker import Moniker, loadMoniker +from OFS.CopySupport import _cb_encode, _cb_decode#, cookie_path +from MosaicBlockInformation import RuleError + + +class MosaicBlock(PortalFolder) : + """ Block class for 'Mosaic Document' """ + + meta_type = 'Mosaic Block' + _properties = ({'id' : 'xpos', 'type' : 'int', 'mode' : 'w'}, + {'id' : 'minimized', 'type' : 'boolean', 'mode' : 'w'},) + + def __init__( self, id, title='', xpos = 0): + PortalFolder.__init__(self, id, title) + self.manage_changeProperties(xpos=xpos) + self.manage_changeProperties(minimized=0) + + security = ClassSecurityInfo() + + + ## Utils methods + + def _getRootBlock(self) : + """Return the root block object ie : + the first block in the tree which have a "_isRootBlock = 1" flag""" + + urlTool = getToolByName(self, 'portal_url') + portalObject = urlTool.getPortalObject() + + block = self + while block != portalObject : + if hasattr(block.aq_self, '_isRootBlock') : + return block + else : + block = block.aq_parent + return None + + def _getSelfRules(self) : + """Return block rules informations""" + mosTool = getToolByName(self, 'mosaic_tool') + myTi = mosTool.getTypeInfo(self) + ruleDic = {} + for rule in myTi.objectValues(['Rule Information']) : + ruleDic[rule.getId()] = rule + return ruleDic + + def _allowedMoves(self, block) : + if type(block) == StringType : + block = getattr(self, block) + rules = self._getSelfRules()[block.portal_type] + move_dic = {'global' : rules.allowMove, + 'rightLeft' : rules.allowMoveRightAndLeft, + 'upDown' : rules.allowMoveUpAndDown, + } + return move_dic + + security.declarePrivate('_getParentRules') + def _getParentRules(self) : + """Return block rules informations""" + mosTool = getToolByName(self, 'mosaic_tool') + parentTi = mosTool.getTypeInfo(self.aq_parent) + return parentTi.objectValues(['Rule Information']) + + def _redirectAfterEdit(self, REQUEST, rootBlock=None, blockId=None) : + if rootBlock is None : + rootBlock = self._getRootBlock() + + if REQUEST.get('ajax') : + url = rootBlock.getActionInfo('object_ajax/edit')['url'] + elif REQUEST.SESSION.get('editBoxes') : + utool = getToolByName(self, 'portal_url') + url = utool() + '/manage_boxes' + else : + url = rootBlock.getActionInfo('object/edit')['url'] + (blockId and '#' + blockId or '') + return REQUEST.RESPONSE.redirect(url) + + security.declareProtected(ModifyPortalContent, 'getAllowedBlocks') + def getAllowedBlocks(self) : + """Return a list with allowed blocks""" + rules = self._getSelfRules() + mosTool = getToolByName(self, 'mosaic_tool') + allowedBlocks = () + for ruleId in rules.keys() : + ti = mosTool.getTypeInfo(ruleId) + try : + if ti.isConstructionAllowed(self) : + allowedBlocks += ({'id' : ti.id, 'title' : ti.title},) + except : + continue + return allowedBlocks + + security.declareProtected(ModifyPortalContent, 'haveRules') + def haveRules(self) : + """ return 1 if type info from self have rules """ + mosTool = getToolByName(self, 'mosaic_tool') + myTi = mosTool.getTypeInfo(self) + if myTi.objectValues(['Rule Information']) : + return 1 + else : + return 0 + + + security.declareProtected(View, 'getSlots') + def getSlots(self) : + """return slots""" + return [ob for ob in self.objectValues() if hasattr(ob, '_isMosaicSlot')] + + security.declareProtected(View, 'getSlotsDic') + def getSlotsDic(self) : + """return slots in dictionary""" + slots = self.getSlots() + slotDic = {} + for slot in slots : + slotDic[slot.getId()] = slot + return slotDic + + security.declarePublic('Title') + def Title(self) : + """Return title""" + return self.getProperty('title', d='') or (hasattr(self, 'caption') and self.caption.text) or '' + + title = Title + + security.declareProtected(ModifyPortalContent, 'setTitle') + def setTitle(self, title) : + if hasattr(self, 'caption') : + self.caption.text = title + self.title = title + + def title_or_id(self) : + """Return title or id""" + return self.Title() or self.id + + ## Methods for displaying + + security.declareProtected(View, 'getBlocksTable') + def getBlocksTable(self, filteredTypes=[]) : + """return blocks ordered in a 2 dimensions table""" + blocks = self.objectValues(['Mosaic Block',]) + + if filteredTypes : + blocks = [block for block in blocks if block.portal_type in filteredTypes] + + #blocks.sort(lambda x, y : cmp(x.xpos, y.xpos)) inutile ??? + rows = 0 + try : + cols = blocks[-1].xpos + except : + cols = 0 + columnsTable = [] # columns list + rules = self._getSelfRules() + for xpos in range(cols + 1) : + colBlockList = [ block for block in blocks if block.xpos == xpos ] # opt : baliser les debuts de colonne + colBlockListLength = len(colBlockList) + + # build a column by iterating over blocks with the position xpos + colBlockInfoList = [] + for blockIndex in range(colBlockListLength) : + block = colBlockList[blockIndex] + blockRule = rules[block.portal_type] + moveDic = {'up' : 0, + 'down' : 0, + 'left' : 0, + 'right' : 0} + if blockRule.allowMoveUpAndDown : + moveDic['up'] = blockIndex > 0 + moveDic['down'] = blockIndex < colBlockListLength - 1 + if blockRule.allowMoveRightAndLeft : + moveDic['left'] = xpos > 0 + moveDic['right'] = 1 + + # every block will be displayed in a cell in a table + colBlockInfoList.append({'block' : block, + 'col' : block.xpos, + 'moves' : moveDic, + 'mode' : blockRule.mode}) + + # append the new column in the column list ie : columnsTable + if colBlockListLength > rows : + rows = colBlockListLength + columnsTable.append(colBlockInfoList) + + # Now the max number of rows in known. + # We can determine rowspan attributes, + # Building lines for an easy iterating over tag + + linesTable = [] + cols += 1 + for lineIndex in range(rows) : + line = [] + for columnIndex in range(cols) : + try : + blockInfo = columnsTable[columnIndex][lineIndex] + if lineIndex == rows - 1 : + blockInfo.update({'lastOne' : 1}) + line.append(blockInfo) + + except : + if lineIndex and linesTable[lineIndex - 1][columnIndex]['block'] is not None : + linesTable[lineIndex - 1][columnIndex].update({'rowspan' : rows - lineIndex + 1, + 'lastOne' : 1}) + + if lineIndex and linesTable[lineIndex - 1][columnIndex].get('rowspan') : + rowspan = -1 # flag for ignoring td insertion + else : + rowspan = rows - lineIndex + line.append({'block' : None, + 'col' : columnIndex, + 'rowspan' : rowspan}) + + linesTable.append(line) + + tableInfo = {'rows' : rows, + 'cols' : cols, + 'lines' : linesTable, + 'columns' : columnsTable} + return tableInfo + + security.declareProtected(View, 'callTemplate') + def callTemplate(self, displayAction='renderer', **kw) : + """ Return block template name from block meta fti """ + mosTool = getToolByName(self, 'mosaic_tool') + mfti = mosTool.getTypeInfo(self) + templateObj = self.restrictedTraverse(mfti.template) + return templateObj(block = self, displayAction = displayAction, **kw) + + security.declareProtected(ModifyPortalContent, 'toggle_minimized') + def toggle_minimized(self, REQUEST = None) : + "toggle minimized property" + if not self.minimized : + self.minimized = 1 + else : + self.minimized = 0 + if REQUEST is not None: + return self._redirectAfterEdit(REQUEST, blockId = self.id) + + security.declareProtected(View, 'ypos') + def ypos(self) : + """ Return the position of self into + the parent block """ + return self.aq_parent.getObjectPosition(self.id) + + ## Block edition methods + + security.declareProtected(ModifyPortalContent, 'addBlock') + def addBlock(self, blockType, xpos, id='', beforeBlock='', afterBlock='', REQUEST=None) : + """ add a new block type """ + mosTool = getToolByName(self, 'mosaic_tool') + blockId = id or str(int(DateTime()))+str(randrange(1000,10000)) + mosTool.constructContent(blockType, + self, + blockId, + xpos=xpos, + beforeBlock=beforeBlock, + afterBlock=afterBlock) + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, blockId = blockId) + else : + return blockId + + security.declareProtected(ModifyPortalContent, 'saveBlock') + def saveBlock(self, REQUEST=None, **kw) : + """ Save block content """ + mosTool = getToolByName(self, 'mosaic_tool') + ti = mosTool.getTypeInfo(self) + slotIds = ti.objectIds(['Slot Information',]) + + if REQUEST is not None: kw.update(REQUEST.form) + dicArgsListItems = [ (argKey, kw[argKey]) for argKey in kw.keys() if type(kw[argKey]) in [InstanceType, DictType] ] + + # iteration over slots for applying edit method + # an exception is raised when a slot name is not defined in the portal type + for slotId, kwords in dicArgsListItems : + if slotId in slotIds : + slot = getattr(self, slotId) # could raise an exception + kwArgs = {} + for k in kwords.keys() : #kwords is an InstanceType not a DictType... + kwArgs[k] = kwords[k] + slot.edit(**kwArgs) + else : + raise KeyError, "this slot : '%s' is not defined in the type information" % slotId + + rootBlock = self._getRootBlock() + if rootBlock is not None : + rootBlock.reindexObject() + + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, rootBlock = rootBlock, blockId = self.getId()) + + security.declareProtected(View, 'SearchableText') + def SearchableText(self) : + blocks = self.objectValues(['Mosaic Block',]) + slots = [ slot for slot in self.objectValues() if hasattr(slot, '_isMosaicSlot') ] + text = '' + + for slot in slots : + text += ' %s' % slot.SearchableText() + for block in blocks : + text += ' %s' % block.SearchableText() + + return text + + security.declareProtected(ModifyPortalContent, 'deleteBlock') + def deleteBlock(self, blockId, REQUEST=None) : + """ Delete the blockId """ + old_pos = self.getObjectPosition(blockId) + if old_pos != 0 : + redirectBlockId = self._objects[old_pos -1]['id'] + else : + redirectBlockId = self.getId() + + self.manage_delObjects([blockId,]) + + if REQUEST : + # évite des appels répétitifs à reindexObject lorsque deleBlock est appelée + # par deleteBlocks + rootBlock = self._getRootBlock() + rootBlock.reindexObject() + return self._redirectAfterEdit(REQUEST, blockId = redirectBlockId) + else : + return redirectBlockId + + security.declareProtected(ModifyPortalContent, 'deleteBlocks') + def deleteBlocks(self, blockIds, REQUEST=None) : + """ Delete block id list""" + redirectBlockId = '' + for blockId in blockIds : + redirectBlockId = self.deleteBlock(blockId) + + rootBlock = self._getRootBlock() + rootBlock.reindexObject() + if REQUEST : + return self._redirectAfterEdit(REQUEST, rootBlock = rootBlock, blockId = redirectBlockId) + + + ## cut and paste methods + + security.declareProtected(ModifyPortalContent, 'pushCp') + def pushCp(self, blockId, REQUEST) : + """ push block in clipboard """ + previousCp = None + oblist = [] + if REQUEST.has_key('__cp') : + previousCp = REQUEST['__cp'] + previousCp = _cb_decode(previousCp) + oblist = previousCp[1] + + block = getattr(self, blockId) + m = Moniker(block) + oblist.append(m.dump()) + cp=(0, oblist) + cp=_cb_encode(cp) + + resp=REQUEST['RESPONSE'] + resp.setCookie('__cp', cp, path='/') + REQUEST['__cp'] = cp + return self._redirectAfterEdit(REQUEST, blockId = blockId) + + security.declareProtected(ModifyPortalContent, 'pasteBlocks') + def pasteBlocks(self, REQUEST) : + """ check rules and paste blocks from cp """ + + if REQUEST.has_key('__cp') : + cp = REQUEST['__cp'] + cp = _cb_decode(cp) + mosTool = getToolByName(self, 'mosaic_tool') + app = self.getPhysicalRoot() + self_fti = getattr(mosTool, self.portal_type) + + + # paste element one by one for + # checking rules on every iteration + for mdata in cp[1] : + m = loadMoniker(mdata) + ob = m.bind(app) + ob_type = ob.portal_type + ob_fti = getattr(mosTool, ob_type) + isBlock = (ob_fti.meta_type == 'Mosaic Block Information') + isRootBlock = getattr(ob, '_isRootBlock', 0) # a block witch have this attribute is a content type + + if isBlock and not isRootBlock and ob_fti.isConstructionAllowed(self) : + # internal copy and paste handling + cp = (0, [m.dump(),]) + cp=_cb_encode(cp) + self.manage_pasteObjects(cb_copy_data = cp) + + _reOrderBlocks(self) + + self.flushCp(REQUEST) + return self._redirectAfterEdit(REQUEST, blockId = self.getId()) + + security.declarePublic('flushCp') + def flushCp(self, REQUEST) : + """ Expire cp cookie """ + REQUEST.RESPONSE.expireCookie('__cp', path='/') + + security.declareProtected(ModifyPortalContent, 'getCpInfos') + def getCpInfos(self, REQUEST) : + """ Return information about loaded objects in cp """ + if REQUEST.has_key('__cp') : + cp = REQUEST['__cp'] + cp = _cb_decode(cp) + return len(cp[1]) + else : + return 0 + + + ## moves methods + + security.declareProtected(ModifyPortalContent, 'moveLeft') + def moveLeft(self, blockId, REQUEST=None) : + """Move block left""" + move_dic = self._allowedMoves(blockId) + if not(move_dic['global'] or move_dic['rightLeft']) : + raise RuleError, "It's not allowed to move this block in this context" + block = getattr(self, blockId) + if block.xpos > 0 : + block.xpos -= 1 + _reOrderBlocks(self) + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, blockId = blockId) + + + + security.declareProtected(ModifyPortalContent, 'moveRight') + def moveRight(self, blockId, REQUEST=None) : + """Move block Right""" + move_dic = self._allowedMoves(blockId) + if not (move_dic['global'] or move_dic['rightLeft']) : + raise RuleError, "It's not allowed to move this block in this context" + block = getattr(self, blockId) + block.xpos += 1 + _reOrderBlocks(self) + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, blockId = blockId) + + + security.declareProtected(ModifyPortalContent, 'moveUp') + def moveUp(self, blockId, REQUEST=None) : + """Move block Up""" + move_dic = self._allowedMoves(blockId) + if not(move_dic['global'] or move_dic['upDown']) : + raise RuleError, "It's not allowed to move this block in this context" + self.moveObjectsUp(blockId) + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, blockId = blockId) + + + security.declareProtected(ModifyPortalContent, 'moveDown') + def moveDown(self, blockId, REQUEST=None) : + """Move block left""" + move_dic = self._allowedMoves(blockId) + if not(move_dic['global'] or move_dic['upDown']) : + raise RuleError, "It's not allowed to move this block in this context" + self.moveObjectsDown(blockId) + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, blockId = blockId) + + security.declareProtected(ModifyPortalContent, 'movesUp') + def movesUp(self, blockIds = [], REQUEST=None) : + """move blocks up""" + for blockId in blockIds : + self.moveUp(blockId) + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, blockId = blockId) + + security.declareProtected(ModifyPortalContent, 'movesDown') + def movesDown(self, blockIds = [], REQUEST=None) : + """move blocks down""" + for blockId in blockIds : + self.moveDown(blockId) + if REQUEST is not None : + return self._redirectAfterEdit(REQUEST, blockId = blockId) + + +InitializeClass(MosaicBlock) + +def _reOrderBlocks(container) : + # This method order blocks. + # It's useful when a left or right move or a block creation in a middle of a column happens. + blocks = list(container.objectValues(['Mosaic Block',])) + + # get the maximum value for xpos attribute + blocks.sort(lambda b1, b2 : cmp(b1.xpos, b2.xpos)) + rows = 0 + try : + cols = blocks[-1].xpos + except : + cols = 0 + + blockPosition = 0 + for xpos in range(cols + 1) : + colBlockList = [ block for block in blocks if block.xpos == xpos ] + colBlockListLength = len(colBlockList) + + for blockIndex in range(colBlockListLength) : + block = colBlockList[blockIndex] + container.moveObjectToPosition(block.getId(), blockPosition) + blockPosition += 1 + + +def addMosaicBlock(dispatcher, id, xpos=0, beforeBlock='', afterBlock='') : + """Add a new mosaicBlock""" + parentBlock = dispatcher.Destination() + parentBlock._setObject(id, MosaicBlock(id, xpos=xpos)) + if beforeBlock : + position = parentBlock.getObjectPosition(beforeBlock) + parentBlock.moveObjectToPosition(id, position) + + elif afterBlock : + position = parentBlock.getObjectPosition(afterBlock) + parentBlock.moveObjectToPosition(id, position + 1) + else : + try : _reOrderBlocks(parentBlock) + except : pass + diff --git a/MosaicBlockInformation.py b/MosaicBlockInformation.py new file mode 100755 index 0000000..f284df7 --- /dev/null +++ b/MosaicBlockInformation.py @@ -0,0 +1,312 @@ +# -*- coding: utf-8 -*- +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + + +from OFS.ObjectManager import ObjectManager +from OFS.SimpleItem import SimpleItem +from OFS.PropertyManager import PropertyManager +from Globals import InitializeClass, DTMLFile +from Products.CMFCore.TypesTool import FactoryTypeInformation +from Products.CMFCore.utils import getToolByName +from types import InstanceType + +from AccessControl import ClassSecurityInfo, Unauthorized +from Products.CMFCore.permissions import View, ModifyPortalContent, \ + ManagePortal + + +class RuleError(Exception) : + def __init__(self, errorMessage) : + self.errorMessage = errorMessage + + def __str__(self) : + return self.errorMessage + +addMosaicBlockInformationForm = DTMLFile('dtml/addMosaicBlockForm', globals()) + +def addMosaicBlockInformation(self, id=None, blockType = '', metaFti = None, REQUEST=None) : + """Add a MosaicBlock""" + mosaicTool = self.mosaic_tool + if blockType : + metaFtis = mosaicTool.getDefaultBlockMetaFtis() + if not metaFtis.has_key(blockType) : + raise ValueError, "Unknown block type : %s" % blockType + else : + blockFti = metaFtis[blockType] + elif metaFti : + blockFti = metaFti.copy() + else : + raise AttributeError, """ + You must specify a default block type or a meta factory type information + """ + if not id : + id = blockFti['id'] + else : + blockFti['id'] = id + + mb = MosaicBlockInformation(blockFti) + self._setObject(id, mb) + + if REQUEST is not None: + REQUEST.RESPONSE.redirect('%s/manage_main' % self.absolute_url()) + return id + + +class MosaicBlockInformation(ObjectManager, FactoryTypeInformation) : + """Base Block""" + + meta_type = "Mosaic Block Information" + + isPrincipiaFolderish = 0 + + security = ClassSecurityInfo() + + _fti_properties = FactoryTypeInformation._properties + + _properties = (_fti_properties[:5] + + ({'id':'template', 'type': 'string', 'mode':'w', + 'label':'Template'}, + {'id':'notify_wf', 'type': 'boolean', 'mode':'w', + 'label':'Notify Workflow Created'},) + + _fti_properties[5:] + ) + + + manage_slotsForm = DTMLFile('dtml/slotsForm', globals()) + manage_rulesForm = DTMLFile('dtml/rulesForm', globals()) + manage_options = (FactoryTypeInformation.manage_options[:1] + + ({'label' : 'Slots', 'action' : 'manage_slotsForm'},) + + ({'label' : 'Rules', 'action' : 'manage_rulesForm'},) + + FactoryTypeInformation.manage_options[1:] + ) + + + def __init__(self, blockFti) : + FactoryTypeInformation.__init__(self, **blockFti) + + # init Slots informations + for slot in blockFti['slots'] : + slotInfo = SlotInfo(slot['id'], slot['type'], slotArgs = slot['args']) + self._setObject(slot['id'], slotInfo) + + # init Rules informations + for rule in blockFti['rules'] : + ruleInfo = RuleInfo(rule['blockType'], rule['maxNbInstance'], rule['allowMove'], rule['mode']) + self._setObject(rule['blockType'], ruleInfo) + + + security.declareProtected(ManagePortal, 'manage_addSlot') + def manage_addSlot(self, id, type, REQUEST=None) : + """Add a slot contruction information""" + slotInfo = SlotInfo(id, type) + self._setObject(id, slotInfo) + if REQUEST : + return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER']) + + security.declareProtected(ManagePortal, 'saveSlots') + def saveSlots(self, REQUEST=None, **kw) : + """Save slots from ZMI form""" + # actualy the ':record' casting in an input form create an InstanceType instead of DictType + kw.update(REQUEST.form) + dicArgsListItems = [ (argKey, kw[argKey]) for argKey in kw.keys() if type(kw[argKey]) == InstanceType ] + for key, args in dicArgsListItems : + slotToEdit = getattr(self, key) + if key != args['id'] : + self.manage_renameObject(key, args['id']) + slotToEdit.id = args['id'] + slotToEdit.type = args['type'] + if REQUEST is not None: + return REQUEST.RESPONSE.redirect(REQUEST['URL1'] + '/manage_slotsForm?manage_tabs_message=Saved changes.') + + security.declareProtected(ManagePortal, 'deleteSlots') + def deleteSlots(self, slotSelection=None, REQUEST=None) : + """Delete slots""" + if slotSelection : + self.manage_delObjects(ids=slotSelection) + if REQUEST : + return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER']) + + + + security.declareProtected(ManagePortal, 'manage_addSlot') + def manage_addRule(self, id, maxNbInstance, allowMove, mode, REQUEST=None) : + """Add a rule information""" + ruleInfo = RuleInfo(id, maxNbInstance, allowMove, mode) + self._setObject(id, ruleInfo) + if REQUEST : + return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER']) + + security.declareProtected(ManagePortal, 'saveSlots') + def saveRules(self, REQUEST=None, **kw) : + """Save rules from ZMI form""" + # actualy the ':record' casting in an input form create an InstanceType instead of DictType + kw.update(REQUEST.form) + dicArgsListItems = [ (argKey, kw[argKey]) for argKey in kw.keys() if type(kw[argKey]) == InstanceType ] + for key, kwords in dicArgsListItems : + ruleToEdit = getattr(self, key) + if key != kwords['id'] : + self.manage_renameObject(key, kwords['id']) + #slotToEdit.id = args['id'] + kwArgs = {} + + for k in kwords.keys() : #kwords is an InstanceType not a DictType... + kwArgs[k] = kwords[k] + + ruleToEdit.edit(**kwArgs) + + if REQUEST is not None: + return REQUEST.RESPONSE.redirect(REQUEST['URL1'] + '/manage_rulesForm?manage_tabs_message=Saved changes.') + + security.declareProtected(ManagePortal, 'deleteSlots') + def deleteRules(self, ruleSelection=None, REQUEST=None) : + """Delete rules""" + if ruleSelection : + self.manage_delObjects(ids=ruleSelection) + if REQUEST : + return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER']) + + + def _checkConstructionRules(self, container) : + """Check """ + typesTool = getToolByName(self, 'mosaic_tool') + container_ti = typesTool.getTypeInfo(container) + rules = container_ti.objectValues(['Rule Information',]) + container_subObjs = container.objectValues() + + if self.getId() in map(lambda x : x.getId(), rules) : + maxNbInstance = container_ti._getOb(self.getId()).maxNbInstance + if maxNbInstance : + if maxNbInstance <= len( [sob for sob in container_subObjs if sob.portal_type == self.getId()]) : + raise RuleError, "It's not allowed to add a new '%s', the quota is exceeded in this block" % \ + self.getId() + else : + return 1 + else : + return 1 # pas de limitation du nombre d'instance + else : + raise RuleError, "'%s' block type construction is not authorized in '%s'" % \ + (self.getId(), container_ti.getId()) + + + security.declarePublic('isConstructionAllowed') + def isConstructionAllowed ( self, container ): + """ + check factory, permissions and rules + """ + if FactoryTypeInformation.isConstructionAllowed ( self, container ) : + typesTool = getToolByName(self, 'mosaic_tool') + container_ti = typesTool.getTypeInfo(container) + if hasattr(container_ti, '_checkConstructionRules') : + try : + return self._checkConstructionRules(container) + except RuleError : + return 0 + return 1 + else : + return 0 + + + def _getFactoryMethod(self, container, check_security=1): + # pas très beau cette surcharge mais + # FactoryTypeInformation ne vérifie pas la + # possibilité de création par un appel à isConstructionAllowed. + + if not check_security : return FactoryTypeInformation._getFactoryMethod(self, container) + + if self.isConstructionAllowed(container) : + return FactoryTypeInformation._getFactoryMethod(self, container) + + raise Unauthorized, ('Cannot create %s' % self.getId()) + + + security.declarePrivate('_finishConstruction') + def _finishConstruction(self, ob) : + """Finish the construction of a content block object.""" + if hasattr(ob, '_setPortalTypeName'): + ob._setPortalTypeName(self.getId()) + + typesTool = getToolByName(self, 'portal_types') + + # Add and init slots in block + for si in self.objectValues(['Slot Information',]) : + kw = {} + for key, value in si.propertyItems() : + kw[key] = value + + typesTool.constructContent(si.type, ob, si.id, **kw) + + if self.notify_wf : + ob.notifyWorkflowCreated() + return ob + + +InitializeClass(MosaicBlockInformation) + +def addSlotInfo(dispatcher, id, type, slotArgs) : + pass + +class SlotInfo(SimpleItem, PropertyManager) : + """Encapsulate slot informations""" + meta_type="Slot Information" + _properties = () + property_extensible_schema__ = 1 + + manage_propertiesForm = DTMLFile('dtml/properties', globals()) + manage_options = PropertyManager.manage_options + SimpleItem.manage_options + + def __init__(self, id, type, slotArgs={}) : + self.id = id + self.type = type + for argId in slotArgs.keys() : + self.manage_addProperty(argId, slotArgs[argId]['value'], slotArgs[argId]['type']) + +InitializeClass(PropertyManager) + + +def addRuleInfo(dispatcher, id, **kw) : + pass + +class RuleInfo(SimpleItem): + """Encapsulate block rule informations""" + meta_type = 'Rule Information' + + def __init__(self, id, maxNbInstance, + allowMove, mode, + allowMoveUpAndDown=0, + allowMoveRightAndLeft=0) : + self.id = id # actualy Block Type name info + self.maxNbInstance = maxNbInstance + if allowMove : + self.allowMoveUpAndDown = 1 + self.allowMoveRightAndLeft = 1 + else : + self.allowMoveUpAndDown = allowMoveUpAndDown + self.allowMoveRightAndLeft = allowMoveRightAndLeft + self.allowMove = allowMove + self.mode = mode + + security = ClassSecurityInfo() + + security.declareProtected(ManagePortal, 'edit') + def edit(self, **kw) : + for key in kw.keys() : + if hasattr(self, key) : + setattr(self, key, kw[key]) + +InitializeClass(RuleInfo) diff --git a/MosaicDocument.py b/MosaicDocument.py new file mode 100755 index 0000000..0720d25 --- /dev/null +++ b/MosaicDocument.py @@ -0,0 +1,132 @@ +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + + + +from AccessControl import ClassSecurityInfo +from Globals import InitializeClass +from Products.CMFCore.permissions import View, ModifyPortalContent, AccessContentsInformation +from Products.CMFCore.PortalContent import PortalContent +from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl +from Products.CMFCore.utils import getToolByName + +from MosaicBlock import MosaicBlock + +factory_type_information = ( + {'id': 'Mosaic Document', + 'title': '_mosaic_document_', + 'content_icon': 'mosaic_icon.png', + 'product': 'MosaicDocument', + 'factory': 'addMosaicDocument', + 'meta_type': 'Mosaic Document', + 'immediate_view': 'mosaicdocument_edit_form', + 'allow_discussion': 0, + 'filter_content_types': 0, + 'actions': ({'id': 'view', + 'name': 'View', + 'action': 'mosaicdocument_view', + 'permissions': (View,)}, + + {'id': 'edit', + 'name': 'Edit', + 'action': 'mosaicdocument_edit_form', + 'permissions': (ModifyPortalContent,)}, + + {'id': 'metadata', + 'name': 'Metadata', + 'action': 'metadata_edit_form', + 'visible': 0, + 'permissions': (ModifyPortalContent, )}, + + ), + }, + ) + + +class MosaicDocument(MosaicBlock, PortalContent, DefaultDublinCoreImpl) : + """ + Mosaic Document + """ + meta_type = 'Mosaic Document' + _isRootBlock = 1 + isPrincipiaFolderish = 0 + + security = ClassSecurityInfo() + + def indexObject(self) : + if getattr(self, 'noIndex', None) : return + PortalContent.indexObject(self) + + unindexObject = PortalContent.unindexObject + + def reindexObject(self, idxs=[]) : + if getattr(self, 'noIndex', None) : return + PortalContent.reindexObject(self, idxs=idxs) + + setTitle = DefaultDublinCoreImpl.setTitle + setDescription = DefaultDublinCoreImpl.setDescription + + Title = DefaultDublinCoreImpl.Title + Description = DefaultDublinCoreImpl.Description + Type = DefaultDublinCoreImpl.Type + + + def __init__(self, id, rules=None, initSubObjects=None, **kw) : + DefaultDublinCoreImpl.__init__(self, **kw) + self.id = id + self._blockRules = rules + self._initSubObjects(initSubObjects) + + def _initSubObjects(self, subObjects) : + pass + + security.declareProtected(View, 'SearchableText') + def SearchableText(self) : + """ + Sets new permissions/roles after this object changed levels + or after one of its levels changed of reviewers/readers. + """ + return '%s %s %s' % (self.title, self.description, MosaicBlock.SearchableText(self)) + + +InitializeClass(MosaicDocument) + +# factory +def addMosaicDocument(dispatcher, id, REQUEST=None, **kw) : + """Ajoute Mosaic Document""" + ob = MosaicDocument(id, **kw) + dispatcher.Destination()._setObject(id, ob) + if REQUEST : + REQUEST.RESPONSE.redirect(dispatcher.DestinationURL() + 'manage_main') + + + + +from Products.CMFCore.Expression import Expression as CMFExpression + +class Expression(CMFExpression) : + def __call__(self, econtext=None) : + """ render the result of expression if an expression context is given + else the source text + """ + if econtext : + return CMFExpression.__call__(self, econtext) + else : + return self.text + + def __len__(self) : return 0 \ No newline at end of file diff --git a/MosaicTool.py b/MosaicTool.py new file mode 100755 index 0000000..9cad0b5 --- /dev/null +++ b/MosaicTool.py @@ -0,0 +1,54 @@ +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +""" + Mosaic Tool +""" + +from Globals import InitializeClass +from Products.CMFCore.TypesTool import TypesTool +from Products.CMFCore.permissions import ManagePortal +from MosaicBlockInformation import MosaicBlockInformation +from AccessControl import ClassSecurityInfo + + +allowedTypes = ['Mosaic Block Information'] + +class MosaicTool(TypesTool): + """ + Mosaic Tool + """ + + id = 'mosaic_tool' + meta_type = 'Mosaic Tool' + + def filtered_meta_types(self, user=None): + # Filters the list of available meta types. + allowed = {} + for name in allowedTypes: + allowed[name] = 1 + + all = TypesTool.inheritedAttribute('filtered_meta_types')(self) + meta_types = [] + for meta_type in self.all_meta_types(): + if allowed.get(meta_type['name']): + meta_types.append(meta_type) + return meta_types + + +InitializeClass(MosaicTool) \ No newline at end of file diff --git a/SpacerSlot.py b/SpacerSlot.py new file mode 100755 index 0000000..9103a5b --- /dev/null +++ b/SpacerSlot.py @@ -0,0 +1,66 @@ +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + + +from Globals import InitializeClass +from Products.CMFCore.permissions import View, ModifyPortalContent +from BaseSlot import BaseSlot +from OFS.SimpleItem import SimpleItem + +factory_type_information = ( {'id' : 'Spacer Slot', + 'meta_type' : 'Spacer Slot', + 'description' : "Spacer Slot for Mosaic Document", + 'icon' : 'mosaic_tool/spacer_icon.gif', + 'product' : 'MosaicDocument', + 'factory' : 'addSpacerSlot', + 'immediate_view' : 'view', + 'actions' : + ({'id' : 'view', + 'name' : 'View', + 'action' : 'slot_spacer_view', + 'permissions' : (View, ) + }, + + {'id' : 'edit', + 'name' : 'Edit', + 'action' : 'slot_spacer_form', + 'permissions' : (ModifyPortalContent, ) + }, + ) + }, + ) + + +class SpacerSlot(BaseSlot, SimpleItem) : + """Spacer Slot""" + meta_type = 'Spacer Slot' + + _editableFields = ('nbOfSpaces',) + _indexableFields = () + + def __init__(self, id, nbOfSpaces = 3) : + self.id = id + self.nbOfSpaces = nbOfSpaces + +InitializeClass(SpacerSlot) + +def addSpacerSlot(dispatcher, id, nbOfSpaces = 3) : + """Add a new SpacerSlot object""" + + o = SpacerSlot(id, nbOfSpaces) + dispatcher.Destination()._setObject(id, o) diff --git a/StringSlot.py b/StringSlot.py new file mode 100755 index 0000000..23531e6 --- /dev/null +++ b/StringSlot.py @@ -0,0 +1,165 @@ +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + + +from Products.CMFCore.permissions import View, ModifyPortalContent + +factory_type_information = ( {'id' : 'String Slot', + 'meta_type' : 'String Slot', + 'description' : "String Slot for Mosaic Document", + 'icon' : 'mosaic_tool/str_icon.gif', + 'product' : 'MosaicDocument', + 'factory' : 'addStringSlot', + 'immediate_view' : 'view', + 'actions' : + ({'id' : 'view', + 'name' : 'View', + 'action' : 'slot_string_view', + 'permissions' : (View, ) + }, + + {'id' : 'edit', + 'name' : 'Edit', + 'action' : 'slot_string_form', + 'permissions' : (ModifyPortalContent, ) + }, + ) + }, + + {'id' : 'Text Slot', + 'meta_type' : 'Text Slot', + 'description' : "Text Slot for Mosaic Document", + 'icon' : 'mosaic_tool/txt_icon.gif', + 'product' : 'MosaicDocument', + 'factory' : 'addStringSlot', + 'immediate_view' : 'view', + 'actions' : + ({'id' : 'view', + 'name' : 'View', + 'action' : 'slot_text_view', + 'permissions' : (View, ) + }, + + {'id' : 'edit', + 'name' : 'Edit', + 'action' : 'slot_text_form', + 'permissions' : (ModifyPortalContent, ) + }, + ) + }, + + {'id' : 'List Slot', + 'meta_type' : 'List Slot', + 'description' : "List Slot for Mosaic Document", + 'icon' : 'mosaic_tool/str_icon.gif', + 'product' : 'MosaicDocument', + 'factory' : 'addStringSlot', + 'immediate_view' : 'view', + 'actions' : + ({'id' : 'view', + 'name' : 'View', + 'action' : 'slot_list_view', + 'permissions' : (View, ) + }, + + {'id' : 'edit', + 'name' : 'Edit', + 'action' : 'slot_text_form', + 'permissions' : (ModifyPortalContent, ) + }, + ) + }, + ) + +from AccessControl import ClassSecurityInfo +from Globals import InitializeClass +from Products.CMFCore.Expression import Expression +from Products.CMFDefault.Document import Document +from OFS.PropertyManager import PropertyManager +from BaseSlot import BaseSlot + +class StringSlot(BaseSlot, Document, PropertyManager) : + """String Slot""" + meta_type = 'String Slot' + _editableFields = ('text', 'text_format', 'size', 'cols', 'rows') + _indexableFields = ('text',) + + manage_options = Document.manage_options[:2] + PropertyManager.manage_options + Document.manage_options[2:] + + _properties = ({'id' : 'choices', 'type' : 'lines', 'mode' : 'w'}, + {'id' : 'castOperator', 'type' : 'string', 'mode' : 'w'}, + {'id' : 'size', 'type' : 'int', 'mode' : 'w'}, + {'id' : 'cols', 'type' : 'int', 'mode' : 'w'}, + {'id' : 'rows', 'type' : 'int', 'mode' : 'w'}, + {'id' : 'url_expression', 'type': 'expr', 'mode' : 'w'}) + + security = ClassSecurityInfo() + + + def __init__(self, id, text='', text_format='plain', + choices = [], castOperator = 'string', size = 60, + cols=50, rows=50, url_expression = None) : + + Document.__init__(self, id, text_format = text_format, text = text) + + self.choices = choices + self.castOperator = castOperator + self.size = size + self.cols = cols + self.rows = rows + if url_expression is None : + self.url_expression = Expression("python:None") + else : + self.url_expression = url_expression + + security.declareProtected(ModifyPortalContent, 'setFormat') + def setFormat(self, format): + """ Set text format and Dublin Core resource format. + """ + value = str(format) + if value == 'text/html' or value == 'html': + self.text_format = 'html' + elif value == 'text/plain': + if self.text_format not in ('structured-text', 'plain'): + self.text_format = 'structured-text' + elif value == 'plain': + self.text_format = 'plain' + else: + self.text_format = 'structured-text' + + +InitializeClass(StringSlot) + + +def addStringSlot(dispatcher, id, text='', text_format='plain', + choices = [], castOperator = 'string', size=60, + cols=50, rows=50, url_expression = None) : + """ + Add a new TextSlot object. + """ + stringSlot = StringSlot(id, + text=text, + text_format=text_format, + choices=choices, + castOperator=castOperator, + size=size, + cols=cols, + rows=rows, + url_expression=url_expression) + parentContainer = dispatcher.Destination() + parentContainer._setObject(id, stringSlot) diff --git a/__init__.py b/__init__.py new file mode 100755 index 0000000..6ccb156 --- /dev/null +++ b/__init__.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau +# (c) 2003 Benoît PIN + +from Products.CMFCore.DirectoryView import registerDirectory +from Products.CMFCore import utils +from Products.CMFCore.permissions import AddPortalContent, ManagePortal + + +import MosaicDocument +import MosaicBlock +import ImageSlot, StringSlot, FileSlot, SpacerSlot + +import MosaicTool +from MosaicBlockInformation import \ + MosaicBlockInformation, addMosaicBlockInformationForm, addMosaicBlockInformation, \ + SlotInfo, addSlotInfo, \ + RuleInfo, addRuleInfo + +contentClasses = ( + MosaicDocument.MosaicDocument, + MosaicBlock.MosaicBlock, + ImageSlot.ImageSlot, + StringSlot.StringSlot, + FileSlot.FileSlot, + SpacerSlot.SpacerSlot + ) + +contentConstructors = ( + MosaicDocument.addMosaicDocument, + MosaicBlock.addMosaicBlock, + ImageSlot.addImageSlot, + StringSlot.addStringSlot, + FileSlot.addFileSlot, + SpacerSlot.addSpacerSlot + ) + +fti = ( + MosaicDocument.factory_type_information + + ImageSlot.factory_type_information + + StringSlot.factory_type_information + + FileSlot.factory_type_information + + SpacerSlot.factory_type_information + + () + ) + +for path in ('default_blocks', 'default_slots', 'skins') : + registerDirectory(path, globals()) + +def initialize(registrar) : + utils.ContentInit( + 'Mosaic Document', + content_types = contentClasses, + permission = AddPortalContent, + extra_constructors = contentConstructors, + fti = fti, + ).initialize(registrar) + + utils.ToolInit( + 'Mosaic Tool', + tools = (MosaicTool.MosaicTool, ), + icon = 'tool.gif', + ).initialize(registrar) + + registrar.registerClass( + MosaicBlockInformation, + constructors = (addMosaicBlockInformationForm, addMosaicBlockInformation), + icon = 'dtml/block_icon.gif') + + registrar.registerClass( + SlotInfo, + constructors=(addSlotInfo,) + ) + + registrar.registerClass( + RuleInfo, + constructors=(addRuleInfo,) + ) + + +## monkey patching ZPublisher.Converters +from ZPublisher.Converters import type_converters +type_converters.update({'expr' : MosaicDocument.Expression}) \ No newline at end of file diff --git a/default_blocks/action_block_icon.gif b/default_blocks/action_block_icon.gif new file mode 100644 index 0000000..6c7cf6c Binary files /dev/null and b/default_blocks/action_block_icon.gif differ diff --git a/default_blocks/action_block_template.pt b/default_blocks/action_block_template.pt new file mode 100755 index 0000000..8d69e3b --- /dev/null +++ b/default_blocks/action_block_template.pt @@ -0,0 +1,54 @@ + + + + + tree_block_template + + + +
+

Action box settings

+
+ + + + + + + + + +
Box title:
title slot here
Categories:
categories slot here
+
+
+
+ + + + + + + +
+ Box title +
+ +
+
+ + + + + \ No newline at end of file diff --git a/default_blocks/block_container_template.pt b/default_blocks/block_container_template.pt new file mode 100755 index 0000000..43d834a --- /dev/null +++ b/default_blocks/block_container_template.pt @@ -0,0 +1,12 @@ +
+
+
+ Appel de block_canvas pour afficher les blocks contenus +
+
+
\ No newline at end of file diff --git a/default_blocks/block_file_template.pt b/default_blocks/block_file_template.pt new file mode 100755 index 0000000..1510303 --- /dev/null +++ b/default_blocks/block_file_template.pt @@ -0,0 +1,14 @@ +
+
+
+
+
+
+
+
+
+
diff --git a/default_blocks/block_image_template.pt b/default_blocks/block_image_template.pt new file mode 100755 index 0000000..9fd20d9 --- /dev/null +++ b/default_blocks/block_image_template.pt @@ -0,0 +1,18 @@ +
+
+
+ + + + +
+
+
+ + + + +
+
\ No newline at end of file diff --git a/default_blocks/block_section_template.pt b/default_blocks/block_section_template.pt new file mode 100755 index 0000000..ae003fc --- /dev/null +++ b/default_blocks/block_section_template.pt @@ -0,0 +1,17 @@ +
+ +
+
+ + + + +
Text
+
+
+

Text

+ +
\ No newline at end of file diff --git a/default_blocks/block_spacer_template.pt b/default_blocks/block_spacer_template.pt new file mode 100755 index 0000000..7646c1c --- /dev/null +++ b/default_blocks/block_spacer_template.pt @@ -0,0 +1,19 @@ +
+ +
+
+ + + + +
Text
+
+
+ + + + +
Text
+
diff --git a/default_blocks/block_text_template.pt b/default_blocks/block_text_template.pt new file mode 100755 index 0000000..543cff3 --- /dev/null +++ b/default_blocks/block_text_template.pt @@ -0,0 +1,19 @@ +
+ +
+
+ + + + +
Text
+
+
+ + + + +
Text
+
\ No newline at end of file diff --git a/default_blocks/block_two_images_template.pt b/default_blocks/block_two_images_template.pt new file mode 100755 index 0000000..c9e50bd --- /dev/null +++ b/default_blocks/block_two_images_template.pt @@ -0,0 +1,24 @@ +
+
+
+ + + + + + +
Image 1 Image 2
+
+
+ + + + + + +
Image 1 Image 2
+
\ No newline at end of file diff --git a/default_blocks/block_utils.pt b/default_blocks/block_utils.pt new file mode 100755 index 0000000..9b1fcc1 --- /dev/null +++ b/default_blocks/block_utils.pt @@ -0,0 +1,63 @@ + + + + + block_utils + + + + +
+
block_form
+
+
+
+
Rendu HTML du block
+ + +
+
+
add_block_form
+
+
+
+
+ + + + Insert new block: + + + + +
+
+ +
+
move_table
+
+ + + + + + + + + + + + +
Move leftMove upMove downMove right CopyPaste 
+ + + \ No newline at end of file diff --git a/default_blocks/getDefaultBlockMetaFtis.py b/default_blocks/getDefaultBlockMetaFtis.py new file mode 100755 index 0000000..0d8e851 --- /dev/null +++ b/default_blocks/getDefaultBlockMetaFtis.py @@ -0,0 +1,301 @@ +##parameters= +View = 'View' +ModifyPortalContent = 'Modify portal content' + + +# Mosaic Document regular fti +mosaicDocument_fti = {'id' : 'Mosaic Document', + 'title' : 'Mosaic Document', + 'content_icon' : 'mosaic_icon.png', + 'product' : 'MosaicDocument', + 'factory' : 'addMosaicDocument', + 'meta_type' : 'Mosaic Document', + 'immediate_view' : 'metadata_edit_form', + 'allow_discussion' : 1, + 'filter_content_types' : 0, + 'actions' : ( + {'id': 'view', + 'title': 'View', + 'action': 'mosaicdocument_view', + 'visible' : 1, + 'permissions': (View,)}, + + {'id': 'edit', + 'title': 'Edit', + 'action': 'mosaicdocument_edit_form', + 'visible' : 1, + 'permissions': (ModifyPortalContent,)}, + + {'id': 'metadata', + 'title': 'Metadata', + 'action': 'metadata_edit_form', + 'visible': 1, + 'permissions': ()},), + } + +# fixed fti part for basic blocks definitions +fixedFtiPart = {'product' : 'MosaicDocument', + 'factory' : 'addMosaicBlock', + 'immediate_view' : 'view', + 'filter_content_types' : 1, + } + +defaultActions = ({'id' : 'view', + 'title' : 'view', + 'action' : 'view', + 'permissions' : (View,), + 'category' : 'object', + },) + +# basic blocks definitions +metaFtis = ( + {'id': 'Image Block', + 'title': 'Image block', + 'icon': 'mosaic_tool/image_block_icon.gif', + 'meta_type': 'Image Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/block_image_template', + 'notify_wf' : 0, + 'slots' : ( + {'id': 'image', + 'type': 'Image Slot', + 'args': + {'thumb_width' : {'type' : 'int', 'value' : 440}, + 'thumb_height' : {'type' : 'int', 'value' : 440}, + 'blankThumbnail' : {'type' : 'string', 'value' : 'mosaic_tool/no_image.jpg'}, + } + }, + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + {'id': 'Two Images Block', + 'title': 'Two images block', + 'icon': 'mosaic_tool/image_block_icon.gif', + 'meta_type': 'Two Images Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/block_two_images_template', + 'notify_wf' : 0, + 'slots' : ( + {'id': 'image', + 'type': 'Image Slot', + 'args': + {'thumb_width' : {'type' : 'int', 'value' : 440}, + 'thumb_height' : {'type' : 'int', 'value' : 440}, + 'blankThumbnail' : {'type' : 'string', 'value' : 'mosaic_tool/no_image.jpg'}, + } + }, + {'id': 'image2', + 'type': 'Image Slot', + 'args': + {'thumb_width' : {'type' : 'int', 'value' : 440}, + 'thumb_height' : {'type' : 'int', 'value' : 440}, + 'blankThumbnail' : {'type' : 'string', 'value' : 'mosaic_tool/no_image.jpg'}, + } + } + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + {'id': 'Text Block', + 'title': 'Text block', + 'icon': 'mosaic_tool/image_block_icon.gif', + 'meta_type': 'Text Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/block_text_template', + 'notify_wf' : 0, + 'slots' : ( + {'id' : 'text', + 'type' : 'Text Slot', + 'args' : + {'text' : {'type' : 'text', 'value' : ''}, + 'text_format' : {'type' : 'string', 'value' : 'structured-text'}, + 'cols' : {'type' : 'int', 'value' : 68}, + 'rows' : {'type' : 'int', 'value' : 20}, + } + }, + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + {'id': 'File Block', + 'title': 'File Block', + 'icon': 'mosaic_tool/image_block_icon.gif', + 'meta_type': 'File Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/block_file_template', + 'notify_wf' : 0, + 'slots' : ( + {'id': 'file', + 'type': 'File Slot', + 'args': {}, + }, + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + {'id': 'Spacer Block', + 'title': 'Spacer block', + 'icon': 'mosaic_tool/image_block_icon.gif', + 'meta_type': 'Spacer Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/block_spacer_template', + 'notify_wf' : 0, + 'slots' : ( + {'id': 'space', + 'type': 'Spacer Slot', + 'args': {'nbOfSpaces' : {'type' : 'int', 'value' : 3},}, + }, + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + {'id': 'Section Block', + 'title': 'Section block', + 'icon': 'mosaic_tool/image_block_icon.gif', + 'meta_type': 'Section Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/block_section_template', + 'notify_wf' : 0, + 'slots' : ( + {'id': 'section', + 'type': 'String Slot', + 'args': + {'text' : {'type' : 'string', 'value' : ''}, + 'size' : {'type' : 'int', 'value' : 60}, + 'choices' : {'type' : 'lines', 'value' : []}, + 'castOperator' : {'type' : 'string', 'value' : 'string'}, + }, + }, + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + + {'id': 'Container Block', + 'title': 'Container block', + 'icon': 'mosaic_tool/image_block_icon.gif', + 'meta_type': 'Container Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'actions' : defaultActions, + }, + +## Boxes blocks + {'id': 'Tree Box Block', + 'title': 'Tree Box Block', + 'icon': 'mosaic_tool/tree_block_icon.gif', + 'meta_type': 'Tree Box Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/tree_block_template', + 'notify_wf' : 0, + 'slots' : ( + {'id': 'rootObject', + 'type': 'String Slot', + 'args': + {'text' : {'type' : 'expr', 'value' : 'portal'}, + 'size' : {'type' : 'int', 'value' : 45}, + 'castOperator' : {'type' : 'string', 'value' : 'expr'}, + }, + }, + {'id': 'filteredMetaTypes', + 'type': 'List Slot', + 'args': + {'text' : {'type' : 'lines', 'value' : 'Plinn Folder'}, + 'cols' : {'type' : 'int', 'value' : 15}, + 'rows' : {'type' : 'int', 'value' : 5}, + 'castOperator' : {'type' : 'string', 'value' : 'lines'}, + }, + }, + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + {'id': 'Action Box Block', + 'title': 'Action Box Block', + 'icon': 'mosaic_tool/action_block_icon.gif', + 'meta_type': 'Action Box Block', + 'filter_content_types': 1, + 'allowed_content_types': (), + 'template' : 'mosaic_tool/action_block_template', + 'notify_wf' : 0, + 'slots' : ( + {'id': 'boxTitle', + 'type': 'String Slot', + 'args': + {'text' : {'type' : 'string', 'value' : ''}, + 'size' : {'type' : 'int', 'value' : 45}, + 'castOperator' : {'type' : 'string', 'value' : 'string'}, + }, + }, + {'id': 'categories', + 'type': 'String Slot', + 'args': + {'text' : {'type' : 'tokens', 'value' : 'global'}, + 'size' : {'type' : 'int', 'value' : 45}, + 'castOperator' : {'type' : 'string', 'value' : 'tokens'}, + }, + }, + ), + 'rules' : (), + 'actions' : defaultActions, + }, + + ) + +metaFtisDic = {} +for mfti in metaFtis : + tempDic = mfti.copy() + mfti.update(fixedFtiPart) + metaFtisDic[tempDic['id']] = mfti + +# adding Mosaic Document block definition +metaFtisDic[mosaicDocument_fti['id']] = mosaicDocument_fti + +def completeMetaFti(dummyFti, + metaFtis, + slots = (), + template = '', + notify_wf = 0) : + allRules = () + for mfti in metaFtis : + rulesDic = {'blockType' : mfti['id'], + 'maxNbInstance' : 0, + 'allowMove' : 1, + 'mode' : 'wd', + }, + allRules += rulesDic + + ftiExtension = {'rules' : allRules, + 'slots' : (), + 'template' : template, + 'notify_wf' : 1, + } + ftiExtension.update(dummyFti) + return ftiExtension + +# complete meta fti for Mosaic Document block definition +metaFtisDic[mosaicDocument_fti['id']] = completeMetaFti(metaFtisDic[mosaicDocument_fti['id']], + metaFtis, + notify_wf = 1) + +# Idem for Container Block definition +metaFtisDic['Container Block'] = completeMetaFti(metaFtisDic['Container Block'], + metaFtis, + template = 'mosaic_tool/block_container_template') + +return metaFtisDic diff --git a/default_blocks/image_block_icon.gif b/default_blocks/image_block_icon.gif new file mode 100644 index 0000000..c6642a2 Binary files /dev/null and b/default_blocks/image_block_icon.gif differ diff --git a/default_blocks/tree_block_icon.gif b/default_blocks/tree_block_icon.gif new file mode 100644 index 0000000..8db851f Binary files /dev/null and b/default_blocks/tree_block_icon.gif differ diff --git a/default_blocks/tree_block_script.js b/default_blocks/tree_block_script.js new file mode 100644 index 0000000..c057e28 --- /dev/null +++ b/default_blocks/tree_block_script.js @@ -0,0 +1,244 @@ +// (c) Benoît PIN 2006 +// http://plinn.org +// Licence GPL + +var TreeMaker; + +(function() { +/* root -> base node (typically a tbody or a table) +* filter -> comma separated list of portal_types +*/ +TreeMaker = function (root, filter, baseImgUrl) { + this.root = root; + var tm = this; + this.root.onclick = function(evt) { tm.refreshTree(evt); }; + this.filter = filter; + this.baseImgUrl = baseImgUrl; + this.depthCpt = new Array(); + this._lastAniImg = null; + + // preload images + var images = ['pl.gif', 'pl_ani.gif', 'mi.gif', 'mi_ani.gif'], img; + for (var i=0 ; i < images.length ; i++) { + img = new Image(); + img.src = this.baseImgUrl + images[i]; + } +} + +/* +* expand / collapse handler +* object loading trigger +*/ +TreeMaker.prototype.refreshTree = function (evt) { + var target = getTargetedObject(evt); + if (target.blur) + target.blur(); + + if (target.tagName == 'IMG') { + target.parentNode.blur(); + var srcParts = target.src.split("/"); + var imgId = srcParts[srcParts.length-1]; + var parentTd = target.parentNode.parentNode; + var parentRow = parentTd.parentNode; + + + switch (imgId) { + case "pl.gif" : + case "pl_ani.gif" : + var linkCell = parentTd.nextSibling; + while (linkCell.nodeType != 1) + linkCell = linkCell.nextSibling; + + var obUrl = linkCell.getElementsByTagName("A")[0].href; + + var req = new XMLHttpRequest(); + var tm = this; + req.onreadystatechange = function() { + switch (req.readyState) { + case 1: + showProgressImage(); + break; + case 4: + hideProgressImage(); + tm.importRows(req, parentRow); + }; + }; + req.open("POST", obUrl + "/xml_nav_tree", true); + req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + req.send("filter=" + this.filter); + + target.src = this.baseImgUrl + "mi_ani.gif"; + this._lastAniImg = target; + + + window.setTimeout(function(){tm._removeLastAniImg();}, 500); + break; + + case "mi.gif" : + case "mi_ani.gif" : + this.removeChildNodes(parentRow); + target.src = this.baseImgUrl + "pl_ani.gif"; + this._lastAniImg = target; + var tm = this; + window.setTimeout(function(){tm._removeLastAniImg();}, 500); + break; + } // end switch (imgId) + disableDefault(evt); + disablePropagation(evt); + } +}; + + +TreeMaker.prototype._removeLastAniImg = function() { + if (this._lastAniImg) { + this._lastAniImg.src = this._lastAniImg.src.replace("_ani", ""); + } +}; + + +/* +* expand the tree: sends request and imports rows based on xml response. +*/ +TreeMaker.prototype.importRows = function(req, parentRow) { + var rows = req.responseXML.documentElement.getElementsByTagName("row"); + var clickedCells = parentRow.getElementsByTagName("TD"); + var row, newRow, indentCell, stateCell, linkCell, img, a, indentValue, colspan, + incTableDepth=false, cols, linkCellColSpan; + + for (var i = 0 ; i < rows.length ; i++ ) { + row = rows[i]; + + + newRow = document.createElement("TR"); + indentCell = document.createElement("TD"); + stateCell = document.createElement("TD"); + stateCell.width = "16"; + linkCell = document.createElement("TD"); + linkCell.width = "99%"; + + + if (clickedCells.length == 3) { + indentValue = parseInt(clickedCells[0].getAttribute("colspan")); + colspan = parseInt(clickedCells[2].getAttribute("colspan")); + } + else { + indentValue = 0; + colspan = parseInt(clickedCells[1].getAttribute("colspan")); + } + + cols = indentValue + colspan; + + if (colspan == 1) + incTableDepth = true; + + indentCell.colSpan = indentValue + 1; + if (!this.depthCpt[indentValue]) + this.depthCpt[indentValue] = 1; + else + this.depthCpt[indentValue] += 1; + + // IE : it's not possible to set colSpan attr to 0 :-((( + linkCellColSpan = cols - indentValue - 1 + if (linkCellColSpan == 0) + linkCell.nullColSpan = true; + else + linkCell.colSpan = linkCellColSpan; + + img = document.createElement("IMG"); + img.src = row.getAttribute("icon"); + img.height = row.getAttribute("height"); + img.width = row.getAttribute("width"); + a = document.createElement("A"); + + a.setAttribute("href", row.getAttribute("url")); + a.setAttribute("title", row.getAttribute("description")); + a.innerHTML = row.childNodes[0].nodeValue; + + if (row.getAttribute("state") == "-1") { + var stateLink = document.createElement("A"); + stateLink.href = "."; + var stateImg = document.createElement("IMG"); + stateImg.src = this.baseImgUrl + "pl.gif"; + stateImg.border = "0"; + stateImg.height = "16"; + stateImg.width = "16"; + stateLink.appendChild(stateImg) + stateCell.appendChild(stateLink); + } + else + stateCell.innerHTML = "  "; + + linkCell.appendChild(img); + linkCell.appendChild(a); + newRow.appendChild(indentCell); + newRow.appendChild(stateCell); + newRow.appendChild(linkCell); + + + this.root.insertBefore(newRow, parentRow.nextSibling); + } //end for + + if (incTableDepth) { + var rows = this.root.getElementsByTagName("TR"); + var cells, lastCell, lastColspan; + for (var i = 0 ; i < rows.length ; i++) { + cells = rows[i].getElementsByTagName("TD"); + lastCell = cells[cells.length - 1]; + + if (lastCell.nullColSpan) { + lastCell.nullColSpan = false; + lastColspan = 0; + } + else + lastColspan = parseInt(lastCell.getAttribute("colspan")); + + lastCell.colSpan = lastColspan + 1; + } + } +}; + +/* +* collapse the tree: removes deeper rows after the 'baseRow' passed. +*/ +TreeMaker.prototype.removeChildNodes = function(baseRow) { + var baseCells = baseRow.getElementsByTagName("TD"); + var baseColSpan = baseCells[baseCells.length-1].colSpan; + var nextRow = baseRow.nextSibling; + var tbody = baseRow.parentNode; + var depthCpt = this.depthCpt; + var nextCells, nextRow2; + + while (nextRow) { + if (nextRow.nodeType == 1) { + nextCells = nextRow.getElementsByTagName("TD"); + if (nextCells.length == 3 && nextCells[2].colSpan < baseColSpan) { + nextRow2 = nextRow.nextSibling; + depthCpt[nextCells[0].colSpan-1] -= 1; + tbody.removeChild(nextRow); + nextRow = nextRow2; + continue; + } + break; + } + nextRow = nextRow.nextSibling; // text node + } + + // recalc colspans for Safari + var maxDepth = depthCpt.length - 1; + var depthReduction = 0; + while (depthCpt[maxDepth - depthReduction] == 0) { + depthCpt.pop(); + depthReduction++; + } + + if (depthReduction) { + var rows = tbody.getElementsByTagName("TR"); + var cells, lastCell, lastColspan; + for (var i = 0 ; i < rows.length ; i++) { + cells = rows[i].getElementsByTagName("TD"); + lastCell = cells[cells.length - 1]; + lastCell.colSpan = parseInt(lastCell.colSpan) - depthReduction; + } + } +}; +})(); \ No newline at end of file diff --git a/default_blocks/tree_block_template.pt b/default_blocks/tree_block_template.pt new file mode 100755 index 0000000..1b45b19 --- /dev/null +++ b/default_blocks/tree_block_template.pt @@ -0,0 +1,92 @@ + + + + + tree_block_template + + + +
+

Tree box settings

+
+ + + + + + + + + + + + + +
Root object expression:
+ rootObject slot here +
+ Filter (meta_type list) : +
filteredMetaTypes slot here
+
+
+
+ + + + + + + + + +
+ + + + + +
+ collapse all + Root Node
+
+ + + + + + + + +
  +    + + Title
+ +
+
+ + + + \ No newline at end of file diff --git a/default_slots/bin_icon.gif b/default_slots/bin_icon.gif new file mode 100644 index 0000000..73eeba4 Binary files /dev/null and b/default_slots/bin_icon.gif differ diff --git a/default_slots/document_icon.gif b/default_slots/document_icon.gif new file mode 100644 index 0000000..028e985 Binary files /dev/null and b/default_slots/document_icon.gif differ diff --git a/default_slots/no_image.jpg b/default_slots/no_image.jpg new file mode 100644 index 0000000..a0dde74 Binary files /dev/null and b/default_slots/no_image.jpg differ diff --git a/default_slots/photo_icon.gif b/default_slots/photo_icon.gif new file mode 100644 index 0000000..f5c451f Binary files /dev/null and b/default_slots/photo_icon.gif differ diff --git a/default_slots/slot_file_form.pt b/default_slots/slot_file_form.pt new file mode 100755 index 0000000..b849a1e --- /dev/null +++ b/default_slots/slot_file_form.pt @@ -0,0 +1,26 @@ + + + slot_file_form + + + +

File

+ Title : + + + ( + Source file: + None + File name + ) +
+ + + + \ No newline at end of file diff --git a/default_slots/slot_file_view.pt b/default_slots/slot_file_view.pt new file mode 100755 index 0000000..075a49a --- /dev/null +++ b/default_slots/slot_file_view.pt @@ -0,0 +1,10 @@ +
+

File

+ Download + (filename) +
\ No newline at end of file diff --git a/default_slots/slot_image_form.pt b/default_slots/slot_image_form.pt new file mode 100755 index 0000000..46327b3 --- /dev/null +++ b/default_slots/slot_image_form.pt @@ -0,0 +1,42 @@ + + + + slot_image_form + + + + +
+
+ width / height: + / +
+
+ Title : + +
+ Change image: +
+ + +
+ + + \ No newline at end of file diff --git a/default_slots/slot_image_view.pt b/default_slots/slot_image_view.pt new file mode 100755 index 0000000..92ef414 --- /dev/null +++ b/default_slots/slot_image_view.pt @@ -0,0 +1,28 @@ + + + slot_image_render + + + +
+ + + +
title
+
+ + \ No newline at end of file diff --git a/default_slots/slot_list_form.pt b/default_slots/slot_list_form.pt new file mode 100755 index 0000000..dc577fc --- /dev/null +++ b/default_slots/slot_list_form.pt @@ -0,0 +1,34 @@ + + + + slot_text_form + + + + + + + + + + + +
+ +
+ +
+ + \ No newline at end of file diff --git a/default_slots/slot_list_view.pt b/default_slots/slot_list_view.pt new file mode 100755 index 0000000..a5d03bf --- /dev/null +++ b/default_slots/slot_list_view.pt @@ -0,0 +1,5 @@ +
+
    +
  • Item
  • +
+
diff --git a/default_slots/slot_spacer_form.pt b/default_slots/slot_spacer_form.pt new file mode 100755 index 0000000..d42e468 --- /dev/null +++ b/default_slots/slot_spacer_form.pt @@ -0,0 +1,18 @@ + + + + slot_spacer_form + + +
+ Number of br: + +
+
+ + \ No newline at end of file diff --git a/default_slots/slot_spacer_view.pt b/default_slots/slot_spacer_view.pt new file mode 100755 index 0000000..82d7d1c --- /dev/null +++ b/default_slots/slot_spacer_view.pt @@ -0,0 +1,5 @@ +
+
+
\ No newline at end of file diff --git a/default_slots/slot_string_form.pt b/default_slots/slot_string_form.pt new file mode 100755 index 0000000..3a36d61 --- /dev/null +++ b/default_slots/slot_string_form.pt @@ -0,0 +1,25 @@ + + + + slot_string_form + + + + + + + \ No newline at end of file diff --git a/default_slots/slot_string_view.pt b/default_slots/slot_string_view.pt new file mode 100755 index 0000000..68bf02a --- /dev/null +++ b/default_slots/slot_string_view.pt @@ -0,0 +1,9 @@ + diff --git a/default_slots/slot_text_form.pt b/default_slots/slot_text_form.pt new file mode 100755 index 0000000..44b73e1 --- /dev/null +++ b/default_slots/slot_text_form.pt @@ -0,0 +1,34 @@ + + + + + slot_text_form + + + + + + + + + + + + +
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/default_slots/slot_text_view.pt b/default_slots/slot_text_view.pt new file mode 100755 index 0000000..468d48e --- /dev/null +++ b/default_slots/slot_text_view.pt @@ -0,0 +1,11 @@ +
+
+

Structured text

+
+
+

plain text

+
+
diff --git a/default_slots/spacer_icon.gif b/default_slots/spacer_icon.gif new file mode 100644 index 0000000..8dca40f Binary files /dev/null and b/default_slots/spacer_icon.gif differ diff --git a/default_slots/str_icon.gif b/default_slots/str_icon.gif new file mode 100644 index 0000000..ae4b6b1 Binary files /dev/null and b/default_slots/str_icon.gif differ diff --git a/default_slots/txt_icon.gif b/default_slots/txt_icon.gif new file mode 100644 index 0000000..48fb186 Binary files /dev/null and b/default_slots/txt_icon.gif differ diff --git a/default_slots/url_icon.gif b/default_slots/url_icon.gif new file mode 100644 index 0000000..08318c0 Binary files /dev/null and b/default_slots/url_icon.gif differ diff --git a/dependencies.txt b/dependencies.txt new file mode 100755 index 0000000..4ce4248 --- /dev/null +++ b/dependencies.txt @@ -0,0 +1,3 @@ +CMF-1.5.5 http://zope.org/Products/CMF +Plinn-1.0 | http://plinn.org +Photo-1.1 | \ No newline at end of file diff --git a/dtml/addMosaicBlockForm.dtml b/dtml/addMosaicBlockForm.dtml new file mode 100755 index 0000000..1f80652 --- /dev/null +++ b/dtml/addMosaicBlockForm.dtml @@ -0,0 +1,49 @@ + + + +

+ Le Mosaic Block regroupe des slots ou bien d'autres blocs. +

+ +
+ + + + + + + + + + + + + + + + + +
+
Id
+
+ +
+
Block type :
+
+ +
+ +
+ +
+
+
+ + diff --git a/dtml/addSlotForm.dtml b/dtml/addSlotForm.dtml new file mode 100755 index 0000000..5383b26 --- /dev/null +++ b/dtml/addSlotForm.dtml @@ -0,0 +1,37 @@ + + + +

+ Le slot est la plus petite pièce du Mosaic Document +

+ +
+ + + + + + + + + + + +
+
+ Id +
+
+ +
+ +
+ +
+
+
+ + diff --git a/dtml/block_icon.gif b/dtml/block_icon.gif new file mode 100644 index 0000000..c2d217b Binary files /dev/null and b/dtml/block_icon.gif differ diff --git a/dtml/icon_installer.gif b/dtml/icon_installer.gif new file mode 100644 index 0000000..a6763de Binary files /dev/null and b/dtml/icon_installer.gif differ diff --git a/dtml/properties.dtml b/dtml/properties.dtml new file mode 100755 index 0000000..f1021cb --- /dev/null +++ b/dtml/properties.dtml @@ -0,0 +1,299 @@ + + + A site-global encoding specification in a property. + Note that this feature only works if there are no unicode objects + around. This means that this feature is not likely to be supported + in all future versions of zope. + + + + + + Thankfully no site-global encoding specification in a property. + We can set UTF-8, and unicode properties will work. + + + + + + + + + + + + + + +

+You are currently working in version &dtml-Principia-Version; +

+
+ +
+ +

+Properties allow you to assign simple values to Zope objects. To change +property values, edit the values and click "Save Changes". +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+   + +
+ Name +
+
+
+ Value +
+
+
+ Type +
+
+ + string:list" value="&dtml-id;" + id="cb-&dtml-id;"> + + + +
+ +
+
+ + + + "> + + "> + + &dtml-type;" size="35" + value=""> + + &dtml-type;" size="35" + value=""> + + CHECKED> + + &dtml-type;" size="35" + value="&dtml-sequence-item; "> + + + + + + + + +
+ +
+ +
+ +
+ +
+ No value for &dtml-select_variable;. +
+
+ + + + +
+ +
+ +
+ +
+ +
+ No value for &dtml-select_variable;. +
+
+ + " size="35" + value=""> + + Unknown property type + + + + +
+ +
+
+ &dtml-type; +
+
+
Warning: be aware that removing 'title' without re-adding it might be dangerous.
+
  +
+ + + +
+
+ + This needs some community review before exposing it officially. + + +  
+ + +

+Properties allow you to assign simple values to Zope objects. There are +currently no properties defined for this item. To add a property, enter a name, type +and value and click the "Add" button. + +

+ +
+
+ + + +
+ +

+To add a new property, enter a name, type and value for the new +property and click the "Add" button. +

+ + + + + + + + + + + + + +
+
+ Name +
+
+ string" size="30" value=""/> + + Type + +
+ +
+
+
+ Value +
+
+ + + + + + +
+ +
+
+
+
+ + + + diff --git a/dtml/rulesForm.dtml b/dtml/rulesForm.dtml new file mode 100755 index 0000000..12d2e1b --- /dev/null +++ b/dtml/rulesForm.dtml @@ -0,0 +1,173 @@ + + + + + + + + &dtml-form_title; + + +

&dtml-form_title;

+
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ "> + + Block type + + + + +
+ Max intances nb + + " + value=""> +
+ Allow Move + + +
+ Allow Move right and left + + +
+ Allow Move up and down + + +
+ Mode + + +

+
+
+
+ + +
+
+
+ + +

Add a rule

+
+ + + + + + + + + + + + + + + + + + + + + +
+ Block type + + +
+ Max intances nb + + +
+ Allow Move + + +
+ Mode + + +
+
+
+
+ + + + + diff --git a/dtml/slot_icon.gif b/dtml/slot_icon.gif new file mode 100644 index 0000000..c2d217b Binary files /dev/null and b/dtml/slot_icon.gif differ diff --git a/dtml/slotsForm.dtml b/dtml/slotsForm.dtml new file mode 100755 index 0000000..f67115f --- /dev/null +++ b/dtml/slotsForm.dtml @@ -0,0 +1,119 @@ + + + + + + + + &dtml-form_title; + + +

&dtml-form_title;

+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ "> + + Id + + " + value=""> +
+ Type + + " + value=""> +
+ Arguments + + + + + + + + + + + + + + + + + +
IdTypeValue
+ +
+

+
+
+
+ + +
+
+
+

Add a slot

+
+ + + + + + + + + + + + +
+ Id + + +
+ Type + + +
+ +
+
+ + + + + diff --git a/exportimport/__init__.py b/exportimport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/exportimport/mosaictool.py b/exportimport/mosaictool.py new file mode 100644 index 0000000..a0a55e1 --- /dev/null +++ b/exportimport/mosaictool.py @@ -0,0 +1,22 @@ +from Products.CMFCore.utils import getToolByName +from Products.GenericSetup.utils import importObjects, exportObjects + +def importMosaicTool(context): + """Import mosaic tool and content types from XML files. + """ + site = context.getSite() + tool = getToolByName(site, 'mosaic_tool') + + importObjects(tool, 'mosaic_tool/', context) + +def exportMosaicTool(context): + """Export mosaic tool content types as a set of XML files. + """ + site = context.getSite() + tool = getToolByName(site, 'mosaic_tool', None) + if tool is None: + logger = context.getLogger('mosaictool') + logger.info('Nothing to export.') + return + + exportObjects(tool, 'mosaic_tool/', context) \ No newline at end of file diff --git a/license.txt b/license.txt new file mode 100755 index 0000000..3912109 --- /dev/null +++ b/license.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/refresh.txt b/refresh.txt new file mode 100755 index 0000000..136d063 --- /dev/null +++ b/refresh.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/skins/block_canvas.pt b/skins/block_canvas.pt new file mode 100755 index 0000000..2b1776e --- /dev/null +++ b/skins/block_canvas.pt @@ -0,0 +1,71 @@ + + + + + block_canvas + + + + + + + + + + + + + + +
+
Formulaire de création du premier block
+
+
+ +
+
Formulaire de création d'un block au dessus + + + + +
+
+ + +
+ tableau des boutons de délacement +
+
+

Rendu HTML du block

+
+
+
Formulaire de création d'un block au dessus + + + + +
+
+
+ +
+
Formulaire de création du premier block + + + +
+
+ +
+ + + \ No newline at end of file diff --git a/skins/collapse_all.ai b/skins/collapse_all.ai new file mode 100644 index 0000000..0b342cc Binary files /dev/null and b/skins/collapse_all.ai differ diff --git a/skins/collapse_all.gif b/skins/collapse_all.gif new file mode 100644 index 0000000..0a282df Binary files /dev/null and b/skins/collapse_all.gif differ diff --git a/skins/copy.png b/skins/copy.png new file mode 100644 index 0000000..0e912de Binary files /dev/null and b/skins/copy.png differ diff --git a/skins/cut.png b/skins/cut.png new file mode 100644 index 0000000..16f336f Binary files /dev/null and b/skins/cut.png differ diff --git a/skins/img_box_delete.png b/skins/img_box_delete.png new file mode 100644 index 0000000..22c443f Binary files /dev/null and b/skins/img_box_delete.png differ diff --git a/skins/img_box_movedown.png b/skins/img_box_movedown.png new file mode 100644 index 0000000..6c4cf13 Binary files /dev/null and b/skins/img_box_movedown.png differ diff --git a/skins/img_box_moveleft.png b/skins/img_box_moveleft.png new file mode 100644 index 0000000..02805f3 Binary files /dev/null and b/skins/img_box_moveleft.png differ diff --git a/skins/img_box_moveright.png b/skins/img_box_moveright.png new file mode 100644 index 0000000..6369aeb Binary files /dev/null and b/skins/img_box_moveright.png differ diff --git a/skins/img_box_moveup.png b/skins/img_box_moveup.png new file mode 100644 index 0000000..f5db18d Binary files /dev/null and b/skins/img_box_moveup.png differ diff --git a/skins/inline_tree_script.dtml b/skins/inline_tree_script.dtml new file mode 100644 index 0000000..0f68fff --- /dev/null +++ b/skins/inline_tree_script.dtml @@ -0,0 +1,5 @@ + diff --git a/skins/make_nav_tree.py b/skins/make_nav_tree.py new file mode 100755 index 0000000..a6b02c9 --- /dev/null +++ b/skins/make_nav_tree.py @@ -0,0 +1,31 @@ +##parameters=rootObject, filtered_meta_types=[], userid='' + +req = context.REQUEST +resp = req.RESPONSE +collapse_all = False +if req.form.get('collapse_all', '') == rootObject.id or req.get('just_login', False) : + collapse_all = True + +from Products.CMFCore.permissions import View + +def getSubObjects(object) : + if getattr(object, 'isPortalContent', False) : + return [] + childs = list(object.listNearestFolderContents(contentFilter={'portal_type':filtered_meta_types}, userid=userid)) + childs.sort(lambda x, y : cmp(x.title_or_id().lower(), y.title_or_id().lower())) + return childs + + + +from ZTUtils import SimpleTreeMaker +stateName = rootObject.id + userid + '_tree' +cookieName = stateName + '-state' +stm = SimpleTreeMaker(stateName) +stm.setChildAccess(function=getSubObjects) +#stm.setStateFunction(expandIfUnauthorized) + +tree, rows = stm.cookieTree(rootObject) +cookieValue = resp.cookies[cookieName]['value'] +resp.setCookie(cookieName, cookieValue, path = '/') + +return {'tree' : tree, 'rows' : rows} \ No newline at end of file diff --git a/skins/mosaic_icon.gif b/skins/mosaic_icon.gif new file mode 100644 index 0000000..9eaf17f Binary files /dev/null and b/skins/mosaic_icon.gif differ diff --git a/skins/mosaic_icon.thumb.gif b/skins/mosaic_icon.thumb.gif new file mode 100644 index 0000000..b3b43ec Binary files /dev/null and b/skins/mosaic_icon.thumb.gif differ diff --git a/skins/mosaicdocument_edit_form.pt b/skins/mosaicdocument_edit_form.pt new file mode 100755 index 0000000..e47016c --- /dev/null +++ b/skins/mosaicdocument_edit_form.pt @@ -0,0 +1,18 @@ + + + + + + + +
+
+ generic_header macro
+
+
+
Paste
+
Block Canvas
+
+ + + \ No newline at end of file diff --git a/skins/mosaicdocument_view.pt b/skins/mosaicdocument_view.pt new file mode 100755 index 0000000..7ccc35a --- /dev/null +++ b/skins/mosaicdocument_view.pt @@ -0,0 +1,19 @@ + + + + + Visualisation d'un document mosaïque + + + +
+
+ generic_header macro
+
+
+
Block Canvas here
+
+ + + \ No newline at end of file diff --git a/skins/paste.png b/skins/paste.png new file mode 100644 index 0000000..05f110b Binary files /dev/null and b/skins/paste.png differ diff --git a/skins/xml_nav_tree.py b/skins/xml_nav_tree.py new file mode 100644 index 0000000..30013fb --- /dev/null +++ b/skins/xml_nav_tree.py @@ -0,0 +1,36 @@ +##parameters=filter='' + +def replaceXMLEntities(text) : + for c, ent in (('<', '<'), ('>', '>'), ('&', '&')) : + text = text.replace(c, ent) + return text + +from string import maketrans +rmBadAttrChars = maketrans('<&"', ' ') + +filter = filter.split(',') + +root = context +context.REQUEST.RESPONSE.setHeader('content-type', 'text/xml; charset=utf-8') +print '' +for ob in context.listNearestFolderContents(contentFilter={'portal_type':filter}) : + icon = context.restrictedTraverse(ob.getIcon()) + state = 0 + if not getattr(ob, 'isPortalContent', False) : + state = ob.listNearestFolderContents(contentFilter={'portal_type':filter}) and "-1" or "0" + row = '%(title)s' % { + 'id' : ob.getId(), + 'url' : ob.absolute_url(), + 'title' : ' '+replaceXMLEntities(ob.title_or_id()), + 'description' : ob.Description().translate(rmBadAttrChars), + 'icon' : icon.absolute_url(), + 'height' : icon.height, + 'width' : icon.width, + 'state' : state + } + print row + + +print '' + +return printed \ No newline at end of file diff --git a/tool.gif b/tool.gif new file mode 100644 index 0000000..a680450 Binary files /dev/null and b/tool.gif differ diff --git a/version.txt b/version.txt new file mode 100755 index 0000000..9f8e9b6 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0 \ No newline at end of file