Copie de MosaicDocument à partir de :
authorBenoît Pin <benoit.pin@gmail.com>
Mon, 25 Oct 2010 11:20:06 +0000 (13:20 +0200)
committerBenoît Pin <benoit.pin@gmail.com>
Mon, 25 Oct 2010 11:20:06 +0000 (13:20 +0200)
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)

78 files changed:
BaseSlot.py [new file with mode: 0755]
FileSlot.py [new file with mode: 0755]
ImageSlot.py [new file with mode: 0755]
MosaicBlock.py [new file with mode: 0755]
MosaicBlockInformation.py [new file with mode: 0755]
MosaicDocument.py [new file with mode: 0755]
MosaicTool.py [new file with mode: 0755]
SpacerSlot.py [new file with mode: 0755]
StringSlot.py [new file with mode: 0755]
__init__.py [new file with mode: 0755]
default_blocks/action_block_icon.gif [new file with mode: 0644]
default_blocks/action_block_template.pt [new file with mode: 0755]
default_blocks/block_container_template.pt [new file with mode: 0755]
default_blocks/block_file_template.pt [new file with mode: 0755]
default_blocks/block_image_template.pt [new file with mode: 0755]
default_blocks/block_section_template.pt [new file with mode: 0755]
default_blocks/block_spacer_template.pt [new file with mode: 0755]
default_blocks/block_text_template.pt [new file with mode: 0755]
default_blocks/block_two_images_template.pt [new file with mode: 0755]
default_blocks/block_utils.pt [new file with mode: 0755]
default_blocks/getDefaultBlockMetaFtis.py [new file with mode: 0755]
default_blocks/image_block_icon.gif [new file with mode: 0644]
default_blocks/tree_block_icon.gif [new file with mode: 0644]
default_blocks/tree_block_script.js [new file with mode: 0644]
default_blocks/tree_block_template.pt [new file with mode: 0755]
default_slots/bin_icon.gif [new file with mode: 0644]
default_slots/document_icon.gif [new file with mode: 0644]
default_slots/no_image.jpg [new file with mode: 0644]
default_slots/photo_icon.gif [new file with mode: 0644]
default_slots/slot_file_form.pt [new file with mode: 0755]
default_slots/slot_file_view.pt [new file with mode: 0755]
default_slots/slot_image_form.pt [new file with mode: 0755]
default_slots/slot_image_view.pt [new file with mode: 0755]
default_slots/slot_list_form.pt [new file with mode: 0755]
default_slots/slot_list_view.pt [new file with mode: 0755]
default_slots/slot_spacer_form.pt [new file with mode: 0755]
default_slots/slot_spacer_view.pt [new file with mode: 0755]
default_slots/slot_string_form.pt [new file with mode: 0755]
default_slots/slot_string_view.pt [new file with mode: 0755]
default_slots/slot_text_form.pt [new file with mode: 0755]
default_slots/slot_text_view.pt [new file with mode: 0755]
default_slots/spacer_icon.gif [new file with mode: 0644]
default_slots/str_icon.gif [new file with mode: 0644]
default_slots/txt_icon.gif [new file with mode: 0644]
default_slots/url_icon.gif [new file with mode: 0644]
dependencies.txt [new file with mode: 0755]
dtml/addMosaicBlockForm.dtml [new file with mode: 0755]
dtml/addSlotForm.dtml [new file with mode: 0755]
dtml/block_icon.gif [new file with mode: 0644]
dtml/icon_installer.gif [new file with mode: 0644]
dtml/properties.dtml [new file with mode: 0755]
dtml/rulesForm.dtml [new file with mode: 0755]
dtml/slot_icon.gif [new file with mode: 0644]
dtml/slotsForm.dtml [new file with mode: 0755]
exportimport/__init__.py [new file with mode: 0644]
exportimport/mosaictool.py [new file with mode: 0644]
license.txt [new file with mode: 0755]
refresh.txt [new file with mode: 0755]
skins/block_canvas.pt [new file with mode: 0755]
skins/collapse_all.ai [new file with mode: 0644]
skins/collapse_all.gif [new file with mode: 0644]
skins/copy.png [new file with mode: 0644]
skins/cut.png [new file with mode: 0644]
skins/img_box_delete.png [new file with mode: 0644]
skins/img_box_movedown.png [new file with mode: 0644]
skins/img_box_moveleft.png [new file with mode: 0644]
skins/img_box_moveright.png [new file with mode: 0644]
skins/img_box_moveup.png [new file with mode: 0644]
skins/inline_tree_script.dtml [new file with mode: 0644]
skins/make_nav_tree.py [new file with mode: 0755]
skins/mosaic_icon.gif [new file with mode: 0644]
skins/mosaic_icon.thumb.gif [new file with mode: 0644]
skins/mosaicdocument_edit_form.pt [new file with mode: 0755]
skins/mosaicdocument_view.pt [new file with mode: 0755]
skins/paste.png [new file with mode: 0644]
skins/xml_nav_tree.py [new file with mode: 0644]
tool.gif [new file with mode: 0644]
version.txt [new file with mode: 0755]

diff --git a/BaseSlot.py b/BaseSlot.py
new file mode 100755 (executable)
index 0000000..e9d19ea
--- /dev/null
@@ -0,0 +1,100 @@
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..4b7c42e
--- /dev/null
@@ -0,0 +1,84 @@
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..2563fbc
--- /dev/null
@@ -0,0 +1,119 @@
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..2904cbf
--- /dev/null
@@ -0,0 +1,546 @@
+# -*- coding: utf-8 -*-
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 <tr> 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 (executable)
index 0000000..f284df7
--- /dev/null
@@ -0,0 +1,312 @@
+# -*- coding: utf-8 -*-
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..0720d25
--- /dev/null
@@ -0,0 +1,132 @@
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..9cad0b5
--- /dev/null
@@ -0,0 +1,54 @@
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..9103a5b
--- /dev/null
@@ -0,0 +1,66 @@
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..23531e6
--- /dev/null
@@ -0,0 +1,165 @@
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License 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 (executable)
index 0000000..6ccb156
--- /dev/null
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
+# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
+
+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 (file)
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 (executable)
index 0000000..8d69e3b
--- /dev/null
@@ -0,0 +1,54 @@
+<html xmlns="http://www.w3.org/1999/xhtml" tal:omit-tag="">
+
+  <head tal:replace="nothing">
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <title>tree_block_template</title>
+  </head>
+
+  <body tal:define="here                               nocall:options/here;
+                                       block                           nocall:options/block;
+                    slots                              block/getSlotsDic;
+                       displayAction           options/displayAction;
+                       boxTitle                        nocall:slots/boxTitle;
+                       categories                      nocall:slots/categories;" tal:omit-tag="" i18n:domain="plinn">
+    <div tal:condition="python:displayAction=='edit'">
+      <h3 i18n:translate="">Action box settings</h3>
+      <form action="." metal:use-macro="here/block_utils/macros/block_form">
+        <table metal:fill-slot="html_block_content">
+          <tr><td i18n:translate="">Box title:</td></tr>
+          <tr>
+            <td tal:content="structure python:boxTitle.callAction(displayAction)">title slot here</td>
+          </tr>
+          <tr><td i18n:translate="">Categories:</td></tr>
+          <tr>
+            <td tal:content="structure python:categories.callAction(displayAction)">categories slot here</td>
+          </tr>
+        </table>
+      </form>
+    </div>
+        <div tal:condition="python:displayAction=='view'">
+<table cellspacing="0"
+       tal:define="allActions python:here.portal_actions.listFilteredActionsFor( here );
+                   filteredActionsList python:[];
+                   dummy python:[ filteredActionsList.extend(l) for l in [ allActions.get(key, []) for key in categories.text or allActions.keys() ] ]"
+       tal:condition="filteredActionsList"
+       tal:attributes="class python:'_'.join(categories.text)">
+          <tr>
+            <th tal:content="structure python:boxTitle.callAction(displayAction)">
+              Box title
+            </th>
+          </tr>
+          <tr>
+            <td>
+              <ul>
+                <li tal:repeat="action filteredActionsList"><a href="." tal:content="action/title" tal:attributes="href action/url" i18n:translate="">action</a></li>
+              </ul>
+            </td>
+          </tr>
+        </table>
+      </div>
+    
+
+  </body>
+
+</html>
\ 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 (executable)
index 0000000..43d834a
--- /dev/null
@@ -0,0 +1,12 @@
+<div tal:define="
+   block nocall:options/block;
+   slots block/getSlotsDic;
+   displayAction options/displayAction;
+   block_class python:displayAction == 'edit' and 'container_block_form' or 'container_block_view';"
+   tal:omit-tag="">
+  <div tal:attributes="class block_class">
+    <div metal:use-macro="here/block_canvas/macros/canvas">
+      Appel de block_canvas pour afficher les blocks contenus
+    </div>
+  </div>
+</div>
\ 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 (executable)
index 0000000..1510303
--- /dev/null
@@ -0,0 +1,14 @@
+<div tal:define="block nocall:options/block;
+                slots block/getSlotsDic;
+                displayAction options/displayAction;"
+     i18n:domain="plinn"> 
+<div tal:condition="python:displayAction == 'edit'" tal:omit-tag="">
+  <form metal:use-macro="here/block_utils/macros/block_form">
+    <div metal:fill-slot="html_block_content"
+         tal:content="structure python:slots['file'].callAction(displayAction)"></div>
+  </form>
+</div>
+<div tal:condition="python:displayAction != 'edit'" tal:omit-tag="">
+  <div tal:content="structure python:slots['file'].callAction(displayAction)"></div>
+</div>
+</div>
diff --git a/default_blocks/block_image_template.pt b/default_blocks/block_image_template.pt
new file mode 100755 (executable)
index 0000000..9fd20d9
--- /dev/null
@@ -0,0 +1,18 @@
+<div tal:define="block nocall:options/block;
+                 slots block/getSlotsDic;
+                 displayAction options/displayAction;" tal:omit-tag="">
+  <div tal:condition="python:displayAction == 'edit'" >
+    <form metal:use-macro="here/block_utils/macros/block_form">
+      <table border="0" cellspacing="4" cellpadding="0" metal:fill-slot="html_block_content">
+        <tr>
+          <td valign="top" tal:content="structure python:slots['image'].callAction(displayAction)"></td>
+        </tr>
+      </table>
+    </form>
+  </div>
+  <table border="0" cellspacing="4" cellpadding="0" tal:condition="python:displayAction != 'edit'">
+    <tr>
+      <td valign="top" tal:content="structure python:slots['image'].callAction(displayAction)"></td>
+    </tr>
+  </table>
+</div>
\ 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 (executable)
index 0000000..ae003fc
--- /dev/null
@@ -0,0 +1,17 @@
+<div tal:define="block nocall:options/block;
+                 slots block/getSlotsDic;
+                 displayAction options/displayAction;" tal:omit-tag="">
+
+  <div tal:condition="python:displayAction == 'edit'">
+    <form metal:use-macro="here/block_utils/macros/block_form">
+      <table border="0" cellspacing="4" cellpadding="0" metal:fill-slot="html_block_content">
+        <tr>
+          <td valign="top" tal:content="structure python:slots['section'].callAction(displayAction)">Text</td>
+        </tr>
+      </table>
+    </form>
+  </div>
+  <h2 tal:condition="python:displayAction != 'edit'"
+      tal:content="structure python:slots['section'].callAction(displayAction)">Text</h2>
+
+</div>
\ 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 (executable)
index 0000000..7646c1c
--- /dev/null
@@ -0,0 +1,19 @@
+<div tal:define="block nocall:options/block;
+                 slots block/getSlotsDic;
+                 displayAction options/displayAction;" tal:omit-tag="">
+                 
+  <div tal:condition="python:displayAction == 'edit'">
+    <form metal:use-macro="here/block_utils/macros/block_form">
+      <table border="0" cellspacing="4" cellpadding="0" metal:fill-slot="html_block_content">
+        <tr>
+          <td valign="top" tal:content="structure python:slots['space'].callAction(displayAction)">Text</td>
+        </tr>
+      </table>
+    </form>
+  </div>
+  <table border="0" cellspacing="4" cellpadding="0" tal:condition="python:displayAction != 'edit'">
+    <tr>
+      <td valign="top" tal:content="structure python:slots['space'].callAction(displayAction)">Text</td>
+    </tr>
+  </table>
+</div>
diff --git a/default_blocks/block_text_template.pt b/default_blocks/block_text_template.pt
new file mode 100755 (executable)
index 0000000..543cff3
--- /dev/null
@@ -0,0 +1,19 @@
+<div tal:define="block nocall:options/block;
+                 slots block/getSlotsDic;
+                 displayAction options/displayAction;" tal:omit-tag="">
+                 
+  <div tal:condition="python:displayAction == 'edit'">
+    <form metal:use-macro="here/block_utils/macros/block_form">
+      <table border="0" cellspacing="4" cellpadding="0" metal:fill-slot="html_block_content">
+        <tr>
+          <td valign="top" tal:content="structure python:slots['text'].callAction(displayAction)">Text</td>
+        </tr>
+      </table>
+    </form>
+  </div>
+  <table border="0" cellspacing="4" cellpadding="0" tal:condition="python:displayAction != 'edit'">
+    <tr>
+      <td valign="top" tal:content="structure python:slots['text'].callAction(displayAction)">Text</td>
+    </tr>
+  </table>
+</div>
\ 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 (executable)
index 0000000..c9e50bd
--- /dev/null
@@ -0,0 +1,24 @@
+<div tal:define="block nocall:options/block;
+                 slots block/getSlotsDic;
+                 displayAction options/displayAction;" tal:omit-tag="">
+  <div tal:condition="python:displayAction == 'edit'">
+    <form metal:use-macro="here/block_utils/macros/block_form">
+      <table border="0" cellspacing="4" cellpadding="0"
+             metal:fill-slot="html_block_content">
+        <tr>
+          <td valign="top" width="215" tal:content="structure python:slots['image'].callAction(displayAction)">Image 1</td>
+          <td width="1">&nbsp;</td>
+          <td valign="top" width="215" tal:content="structure python:slots['image2'].callAction(displayAction)">Image 2</td>
+        </tr>
+      </table>
+    </form>
+  </div>
+  <table border="0" cellspacing="4" cellpadding="0"
+         tal:condition="python:displayAction != 'edit'">
+    <tr>
+      <td valign="top" width="215" tal:content="structure python:slots['image'].callAction(displayAction)">Image 1</td>
+      <td width="1">&nbsp;</td>
+      <td valign="top" width="215" tal:content="structure python:slots['image2'].callAction(displayAction)">Image 2</td>
+    </tr>
+  </table>
+</div>
\ No newline at end of file
diff --git a/default_blocks/block_utils.pt b/default_blocks/block_utils.pt
new file mode 100755 (executable)
index 0000000..9b1fcc1
--- /dev/null
@@ -0,0 +1,63 @@
+<html>
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+    <title>block_utils</title>
+    <link href="../../CriSIT/default_skins/main_stylesheet.css.css" rel="stylesheet" media="screen">
+  </head>
+
+  <body bgcolor="#ffffff" i18n:domain="plinn">
+<br/>
+<pre>block_form</pre>
+<br/>
+<hr/>
+    <form metal:define-macro="block_form" class="block_form" action="."
+          method="post" enctype="multipart/form-data"
+          tal:attributes="action block/absolute_url;
+                          class python:exists('block_form_class') and block_form_class or 'block_form' ">
+      <div metal:define-slot="html_block_content">Rendu HTML du block</div>
+      <input type="submit" value="Valider"
+             name="saveBlock:method"
+             tal:attributes="value python:exists('submit_action') and submit_action or 'Validate'"
+             i18n:attributes="value" />
+      <input type="hidden" name="noAjax" value="1" tal:condition="request/SESSION/editBoxes|nothing" />
+    </form>
+<br/>
+<pre>add_block_form</pre>
+<br/>
+<hr/>
+    <div metal:define-macro="add_block_form" tal:define="allowedBlocks baseBlock/getAllowedBlocks" tal:omit-tag="">
+    <form action="." method="get"
+          tal:attributes="action baseBlock/absolute_url">
+      <span metal:define-slot="additional_fields">
+        <input type="hidden" name="xpos:int" value="0" />
+      </span>
+      <span i18n:translate="">Insert new block: 
+      </span>
+      <select name="blockType" class="mosaic_input">
+       <option tal:repeat="bt allowedBlocks" tal:attributes="value bt/id" tal:content="bt/title" i18n:translate="">BlokcType</option>
+      </select> 
+      <input type="submit" name="addBlock:method" value="Add" i18n:attributes="value" />
+      <input type="hidden" name="noAjax" value="1" tal:condition="request/SESSION/editBoxes|nothing" />
+      </form>
+    </div>
+    
+<br/>
+<pre>move_table</pre>
+    <br/>
+    <table cellspacing="2" cellpadding="0" metal:define-macro="move_table" class="move_table">
+      <tr>
+        <td tal:condition="blockInfo/moves/left"><a href="." tal:attributes="href string:${baseBlock/absolute_url}/moveLeft?blockId=${block/getId}" title="Move right" i18n:attributes="title"><img src="../skins/img_box_moveleft.png" alt="Move left" name="moveLeft:method" height="12" width="12" border="0" tal:attributes="src string:img_box_moveleft.png" i18n:attributes="alt"/></a></td>
+        <td tal:condition="blockInfo/moves/up"><a href="." tal:attributes="href string:${baseBlock/absolute_url}/moveUp?blockId=${block/getId}" title="Move up" i18n:attributes="title"><img src="../skins/img_box_moveup.png" alt="Move up" name="moveUp:method" height="12" width="12" border="0" tal:attributes="src string:img_box_moveup.png" i18n:attributes="alt"/></a></td>
+        <td tal:condition="blockInfo/moves/down"><a href="." tal:attributes="href string:${baseBlock/absolute_url}/moveDown?blockId=${block/getId}" title="Move down" i18n:attributes="title"><img src="../skins/img_box_movedown.png" alt="Move down" name="moveDown:method" height="12" width="12" border="0" tal:attributes="src string:img_box_movedown.png" i18n:attributes="alt"/></a></td>
+        <td tal:condition="blockInfo/moves/right"><a href="." tal:attributes="href string:${baseBlock/absolute_url}/moveRight?blockId=${block/getId}" title="Move right" i18n:attributes="title"><img src="../skins/img_box_moveright.png" alt="Move right" name="moveRight:method" height="12" width="12" border="0" tal:attributes="src string:img_box_moveright.png" i18n:attributes="alt"/></a></td>
+        <td width="20px"> </td>
+        <td><a href="." tal:attributes="href string:${baseBlock/absolute_url}/pushCp?blockId=${block/getId}" title="Copy" i18n:attributes="title"><img src="../skins/copy.png" alt="Copy" height="13" width="15" border="0" tal:attributes="src string:copy.png" i18n:attributes="alt"/></a></td>
+        <td tal:condition="python:here.cb_dataValid() and block.haveRules()"><a href="." tal:attributes="href string:${block/absolute_url}/pasteBlocks" title="Paste" i18n:attributes="title"><img src="../skins/paste.png" alt="Paste" height="14" width="16" border="0" tal:attributes="src string:paste.png" i18n:attributes="alt"/></a></td>
+        <td width="67" tal:condition="python:'d' in blockInfo['mode']"> </td>
+        <td tal:condition="python:'d' in blockInfo['mode']"><a href="." tal:attributes="href string:${baseBlock/absolute_url}/deleteBlock?blockId=${block/getId}" title="Delete" i18n:attributes="title"><img src="../skins/img_box_delete.png" alt="" height="12" width="12" border="0" tal:attributes="src string:img_box_delete.png"/></a></td>
+      </tr>
+    </table>
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/default_blocks/getDefaultBlockMetaFtis.py b/default_blocks/getDefaultBlockMetaFtis.py
new file mode 100755 (executable)
index 0000000..0d8e851
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..c057e28
--- /dev/null
@@ -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 = "&nbsp;&nbsp;";
+               
+               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 (executable)
index 0000000..1b45b19
--- /dev/null
@@ -0,0 +1,92 @@
+<html xmlns="http://www.w3.org/1999/xhtml" tal:omit-tag="">
+
+  <head tal:replace="nothing">
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <title>tree_block_template</title>
+  </head>
+
+  <body tal:define="block nocall:options/block;
+                    slots block/getSlotsDic;
+                    displayAction options/displayAction;
+                    rootObject nocall:slots/rootObject;
+                    filteredMetaTypes nocall:slots/filteredMetaTypes;"
+        tal:omit-tag=""
+        i18n:domain="plinn">
+    <div tal:condition="python:displayAction=='edit'" tal:omit-tag="">
+      <h3 i18n:translate="">Tree box settings</h3>
+      <form action="." metal:use-macro="here/block_utils/macros/block_form">
+        <table border="0" cellspacing="4" cellpadding="0" metal:fill-slot="html_block_content">
+          <tr>
+            <td i18n:translate="">Root object expression:</td>
+          </tr>
+          <tr>
+            <td valign="top" tal:content="structure python:rootObject.callAction(displayAction)">
+              rootObject slot here
+            </td>
+          </tr>
+          <tr>
+            <td valign="top" i18n:translate="">
+              Filter (meta_type list) :
+            </td>
+          </tr>
+          <tr>
+            <td valign="top" tal:content="structure python:filteredMetaTypes.callAction(displayAction)">filteredMetaTypes slot here</td>
+          </tr>
+        </table>
+      </form>
+    </div>
+    <div tal:define="treeRoot   python:rootObject.text(rootObject.getExprContext());
+                     treeDict   python:block.make_nav_tree(rootObject=treeRoot, filtered_meta_types=filteredMetaTypes.text );
+                     tree       treeDict/tree;
+                     rows       treeDict/rows;
+                     height                    tree/height;
+                     rootNode   python:rows.pop(0);
+                     box_id string:tree_box_${rootNode/object/id};
+                     portal_url here/portal_url"
+         tal:condition="python:displayAction=='view' and rows"
+         tal:omit-tag=""
+         tal:on-error="string:Path Error">
+      <script type="text/javascript" tal:attributes="src here/tree_block_script.js/absolute_url"></script>
+                       <table class="tree_box" cellspacing="0">
+                               <tr>
+                                       <th>
+                                               <table>
+                                                       <tr>
+                                                               <td width="16" tal:condition="nothing">
+                                                                       <a title="collapse all" href="." tal:attributes="href string:?collapse_all=${rootNode/object/id}" i18n:attributes="title"><img src="../skins/collapse_all.gif" alt="collapse all" height="16" width="16" border="0" tal:attributes="src here/collapse_all.gif/absolute_url" i18n:attributes="alt" /></a>
+                                                               </td>
+                                                               <th tal:content="rootNode/object/title_or_id" style="border : none">Root Node</th>
+                                                       </tr>
+                                               </table>
+                                       </th>
+                               </tr>
+
+                               <tr>
+                                       <td>
+                                               <table  cellspacing="0">
+                                                       <tbody tal:attributes="id  box_id">
+                                                               <tr tal:repeat="row rows">
+                                                               <td tal:define="indent python: row.depth - 1" tal:attributes="colspan indent" tal:condition="indent">&nbsp;</td>
+                                                               <td tal:define="state row/state ; rlink row/branch ; msg python:state > 0 and 'collapse' or 'expand'" width="16"
+                                                                ><a href="#" tal:condition="state" tal:attributes="name row/id ; href rlink/link ; title msg" i18n:attributes="title"
+                                                                ><img src="/p_/pl" alt="" tal:attributes="src python:'%s/%s' % (portal_url, state > 0 and 'mi.gif' or 'pl.gif') ; alt msg" i18n:attributes="alt" border="0" height="16" width="16" /></a>
+                                                                 <a href="#" tal:attributes="name row/id ; href rlink/link" tal:condition="not:state">&nbsp;&nbsp;</a>
+                                                               </td>
+                                                               <td colspan="1" tal:define="obj nocall: row/object; url obj/absolute_url; description obj/Description"
+                                                                               tal:attributes="colspan python: height - row.depth"
+                                                                               width="99%" tal:on-error="string:erreur"
+                                                                ><a href="." tal:attributes="href url; title description"
+                                                                ><img src="." alt="" tal:define="icon python:here.restrictedTraverse(obj.icon())" tal:attributes="src icon/absolute_url; height icon/height; width icon/width" border="0" />
+                                                                       <span tal:replace="obj/title_or_id">Title</span></a></td>
+                                                               </tr>
+                                                       </tbody>
+                                               </table>
+                                               <tal:include content="structure python:here.inline_tree_script(box_id=box_id, filter_str=','.join(filteredMetaTypes.text))" />
+                                       </td>
+                               </tr>
+                       </table>
+    </div>
+    
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/default_slots/bin_icon.gif b/default_slots/bin_icon.gif
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (executable)
index 0000000..b849a1e
--- /dev/null
@@ -0,0 +1,26 @@
+<html tal:omit-tag="">
+  <head tal:replace="nothing">
+    <title>slot_file_form</title>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+  </head>
+  <body tal:define="object nocall:options/object;
+                    title python:object and object.title or ''"
+        i18n:domain="plinn"
+        tal:omit-tag="">
+               <h3 i18n:translate="" style="display : inline">File</h3>
+    <span i18n:translate="" tal:omit-tag="">Title</span> : 
+    <input type="text" class="mosaic_input"
+           tal:attributes="name python:'.'.join((object.getId(), 'title')) + ':record:string' ;
+                           value title" />
+    <span>
+     (
+      <span i18n:translate="" tal:omit-tag="">Source file: </span>
+      <span tal:condition="python:object.filename is None or object.filename == '' or object.filename == 'file'" i18n:translate="">None</span>
+      <span tal:condition="python:not(object.filename is None or object.filename == '' or object.filename == 'file')" tal:content="object/filename">File name</span>
+      )
+    </span><br/>
+    <input type="file" size="16" class="mosaic_input"
+           tal:attributes="name python:'.'.join((object.getId(), 'file')) + ':record'" />
+    <input type="hidden" name="noAjax" value="1" />
+  </body>
+</html>        
\ 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 (executable)
index 0000000..075a49a
--- /dev/null
@@ -0,0 +1,10 @@
+<div tal:define="object nocall:options/object;
+                 show_type options/show_type|python:1;
+                 show_preview options/show_preview|python:1;
+                 link_title python:object.title or object.filename;
+                 filename python:object.title and object.filename;"
+      i18n:domain="plinn">
+      <h3 i18n:translate="" style="display:inline">File</h3>
+      <a target="_blank" tal:attributes="href string:${object/absolute_url}/index_html" tal:content="link_title">Download</a>
+      (<span tal:condition="filename" tal:replace="filename">filename</span>)
+</div>
\ 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 (executable)
index 0000000..46327b3
--- /dev/null
@@ -0,0 +1,42 @@
+<html tal:omit-tag="">
+
+  <head tal:replace="nothing">
+    <title>slot_image_form</title>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+  </head>
+
+  <body tal:define="object nocall:options/object;
+                    thumbnail object/thumbnail;
+                    width thumbnail/width;
+                    height thumbnail/height;
+                    showSize object/get_size;
+                    src string:${object/absolute_url}/thumbnail;
+                    title python:object and object.title or ''"
+        i18n:domain="plinn"
+        tal:omit-tag="">
+    <div>
+       <div tal:condition="showSize" tal:omit-tag="">
+       <span i18n:translate="" tal:omit-tag="">width / height:</span>
+       <input type="text" name="textfieldName" size="4" class="mosaic_input"
+             tal:attributes="name python:'.'.join((object.getId(), 'thumb_width')) + ':record:int'; value width;"
+             tal:condition="showSize" /> / 
+      <input type="text" name="textfieldName" size="4" class="mosaic_input"
+             tal:attributes="name python:'.'.join((object.getId(), 'thumb_height')) + ':record:int';
+                             value height;" /><br/></div>
+      <img tal:attributes="src src;
+                           height height;
+                           width width;
+                           alt python:title or 'place holder for an image'" /><br/>
+      <small i18n:translate="">Title :</small>
+      <input type="text" class="mosaic_input"
+       tal:attributes="name python:'.'.join((object.getId(), 'title')) + ':record:string' ; value title" />
+      <br/>
+      <span i18n:translate="">Change image:</span>
+      <br/>
+      <input class="mosaic_input" type="file"
+             tal:attributes="name python:'.'.join((object.getId(), 'file')) + ':record'" />
+      <input type="hidden" name="noAjax" value="1" />
+    </div>
+  </body>
+
+</html>
\ 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 (executable)
index 0000000..92ef414
--- /dev/null
@@ -0,0 +1,28 @@
+<html tal:omit-tag="">
+<head tal:replace="nothing">
+    <title>slot_image_render</title>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+  </head>
+<body tal:define="object nocall:options/object;
+                  thumbnail object/thumbnail;
+                  src thumbnail/absolute_url;
+                  width thumbnail/width;
+                  height thumbnail/height;
+                  title object/title|nothing;"
+      i18n:domain="plinn"
+      tal:omit-tag="">
+  <div class="image">
+    <a title="Click to see image full-size" target="image_view" i18n:attributes="title"
+       tal:attributes="href string:${object/absolute_url}/getJpegImage;
+       onClick string:window.open('${object/absolute_url}/getJpegImage', 'image_view', 'toolbar=no,scrollbars=yes,,resizable=1,width=${object/width},height=${object/height}')">
+      <img border="0"
+        tal:attributes="
+          width width;
+          height height;
+          src src;
+          alt python:title or ''" />
+    </a>
+      <div tal:condition="title" tal:content="title" class="caption">title</div>
+    </div>
+</body>
+</html>
\ 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 (executable)
index 0000000..dc577fc
--- /dev/null
@@ -0,0 +1,34 @@
+<html tal:omit-tag="">
+  <head tal:replace="nothing">
+    <meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
+    <title>slot_text_form</title>
+  </head>
+  <body tal:define="object nocall:options/object;
+                    cols object/cols;
+                    rows object/rows;
+                    text object/text|nothing;"
+        tal:omit-tag="">
+    <table border="0" cellspacing="2" cellpadding="0">
+      <tr>
+        <td align="center">
+          <input type="text" name="cols:int" size="2" tabindex="2" class="mosaic_input"
+                 tal:attributes="name python:'.'.join((object.id, 'cols')) + ':record:int';
+                                 value cols" />
+        </td>
+        <td></td>
+      </tr>
+      <tr>
+        <td><textarea tabindex="1" tal:attributes="name python:'.'.join((object.id, 'text')) + ':record:text'  ;
+                              cols cols ;
+                              rows rows"
+              class="mosaic_input"
+              tal:content="text"></textarea></td>
+        <td>
+          <input type="text" name="rows:int" size="2"  class="mosaic_input"
+                 tal:attributes="name python:'.'.join((object.id, 'rows')) + ':record:int';
+                                 value rows" />
+        </td>
+      </tr>
+    </table>
+  </body>
+</html>
\ 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 (executable)
index 0000000..a5d03bf
--- /dev/null
@@ -0,0 +1,5 @@
+<div tal:define="object nocall:options/object;">
+  <ul>
+    <li tal:repeat="item object/text" tal:content="item">Item</li>
+   </ul>
+</div>
diff --git a/default_slots/slot_spacer_form.pt b/default_slots/slot_spacer_form.pt
new file mode 100755 (executable)
index 0000000..d42e468
--- /dev/null
@@ -0,0 +1,18 @@
+<html tal:omit-tag="">
+  <head tal:replace="nothing">
+    <meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
+    <title>slot_spacer_form</title>
+  </head>
+  <body tal:define="object nocall:options/object;
+                    nbOfSpaces object/nbOfSpaces;"
+        i18n:domain="plinn"
+        tal:omit-tag="">
+    <div>
+      <span i18n:translate="" tal:omit-tag="">Number of br:</span>
+      <input type="text" name="cols:int" size="2" tabindex="2" class="mosaic_input"
+                 tal:attributes="name python:'.'.join((object.id, 'nbOfSpaces')) + ':record:int';
+                                 value nbOfSpaces" />
+    </div>
+    <br tal:repeat="br python:range(nbOfSpaces)" />
+  </body>
+</html>
\ 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 (executable)
index 0000000..82d7d1c
--- /dev/null
@@ -0,0 +1,5 @@
+<div tal:define="object nocall:options/object;
+                 nbOfSpaces object/nbOfSpaces"
+     tal:omit-tag="">
+  <br tal:repeat="br python:range(nbOfSpaces)" />
+</div>
\ 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 (executable)
index 0000000..3a36d61
--- /dev/null
@@ -0,0 +1,25 @@
+<html tal:omit-tag="">
+  <head tal:replace="nothing">
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+    <title>slot_string_form</title>
+  </head>
+  <body tal:define="object nocall:options/object;
+                    size object/size;
+                    castOperator object/castOperator;
+                    choices object/choices;
+                    text python:(castOperator == 'tokens' and [' '.join(object.text),] or [object.text,])[0];"
+        tal:omit-tag="">
+    <input type="text" class="mosaic_input"
+           tal:condition="not:choices"
+           tal:attributes="name python:'.'.join((object.getId(), 'text')) + ':'.join(('', 'record', castOperator)) ;
+                           size size;
+                           value text;" />
+                           
+     <select tal:condition="choices" class="mosaic_input"
+             tal:attributes="name python:'.'.join((object.getId(), 'text')) + ':'.join(('', 'record', castOperator)) ; ">
+      <option tal:attributes="value choice ;
+                              selected python:str(choice) == str(text)"
+              tal:content="choice" tal:repeat="choice choices">Choice</option>
+    </select>
+  </body>
+</html>
\ 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 (executable)
index 0000000..68bf02a
--- /dev/null
@@ -0,0 +1,9 @@
+<div tal:define="object nocall:options/object;
+                 text object/text;
+                 ec object/getExprContext;
+                 url python:object.url_expression(ec);"
+     tal:omit-tag="">
+  <a href="." tal:omit-tag="not:url" tal:attributes="href url">
+    <span tal:replace="text">text</span>
+  </a>
+</div>
diff --git a/default_slots/slot_text_form.pt b/default_slots/slot_text_form.pt
new file mode 100755 (executable)
index 0000000..44b73e1
--- /dev/null
@@ -0,0 +1,34 @@
+<html xmlns="http://www.w3.org/1999/xhtml" tal:omit-tag="">
+
+  <head tal:replace="nothing">
+    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+    <title>slot_text_form</title>
+  </head>
+
+  <body tal:define="object nocall:options/object;
+                    cols object/cols;
+                    rows object/rows;
+                    castOperator object/castOperator|string:text;
+                    text python:(castOperator=='lines' and ['\n'.join(object.text),] or [object.text,])[0]" tal:omit-tag="">
+    <table border="0" cellspacing="2" cellpadding="0">
+      <tr>
+        <td align="center">
+          <input class="mosaic_input" type="text" name="cols:int" size="2"
+                tal:attributes="name python:'.'.join((object.id, 'cols')) + ':record:int'; value cols"  />
+        </td>
+        <td></td>
+      </tr>
+      <tr>
+        <td>
+          <textarea class="mosaic_input" tabindex="1" tal:attributes="name python:'.'.join((object.id, 'text')) + ':record:' + castOperator  ;
+                              cols cols ;
+                              rows rows" tal:content="text"></textarea>
+        </td>
+        <td>
+          <input class="mosaic_input" type="text" name="rows:int" size="2" tal:attributes="name python:'.'.join((object.id, 'rows')) + ':record:int'; value rows" />
+        </td>
+      </tr>
+    </table>
+  </body>
+
+</html>
\ 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 (executable)
index 0000000..468d48e
--- /dev/null
@@ -0,0 +1,11 @@
+<div tal:define="object nocall:options/object;
+                 text_format object/text_format;">
+    <div tal:condition="python:text_format == 'structured-text'"
+         class="slot_text">
+      <p tal:replace="structure python:here.portal_transforms.convert('st_to_html', object.text)">Structured text</p>
+    </div>
+    <div tal:condition="python:text_format != 'structured-text' "
+         class="slot_text">
+      <p tal:replace="structure object/text">plain text</p>
+    </div>
+</div>
diff --git a/default_slots/spacer_icon.gif b/default_slots/spacer_icon.gif
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (executable)
index 0000000..4ce4248
--- /dev/null
@@ -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 (executable)
index 0000000..1f80652
--- /dev/null
@@ -0,0 +1,49 @@
+<dtml-var manage_page_header>
+
+<dtml-var "manage_form_title(this(), _,
+           form_title='Add Mosaic Block'
+          )">
+<p class="form-help">
+  Le Mosaic Block regroupe des slots ou bien d'autres blocs.
+</p>
+
+<FORM ACTION="addMosaicBlockInformation" METHOD="POST">
+
+<table cellspacing="0" cellpadding="2" border="0">
+  <tr>
+    <td align="left" valign="top">
+      <div class="form-label">Id</div>
+    </td>
+    <td align="left" valign="top">
+      <input type="text" name="id" size="40" />
+    </td>
+  </tr>
+
+  <tr>
+    <td align="left" valign="top">
+      <div class="form-label">Block type : </div>
+    </td>
+    <td align="left" valign="top">
+     <select name="blockType" size="1">
+       <dtml-in "mosaic_tool.getDefaultBlockMetaFtis().keys()">
+          <option value="<dtml-var sequence-item>"><dtml-var sequence-item></option>
+       </dtml-in>
+      </select>
+    </td>
+  </tr>
+
+
+  <tr>
+    <td align="left" valign="top">
+    </td>
+    <td align="left" valign="top">
+    <div class="form-element">
+    <input class="form-element" type="submit" name="submit" 
+     value="Add" /> 
+    </div>
+    </td>
+  </tr>
+</table>
+</form>
+
+<dtml-var manage_page_footer>
diff --git a/dtml/addSlotForm.dtml b/dtml/addSlotForm.dtml
new file mode 100755 (executable)
index 0000000..5383b26
--- /dev/null
@@ -0,0 +1,37 @@
+<dtml-var manage_page_header>
+
+<dtml-var "manage_form_title(this(), _,
+           form_title='Add Slot'
+          )">
+<p class="form-help">
+  Le slot est la plus petite pièce du Mosaic Document
+</p>
+
+<FORM ACTION="addSlot" METHOD="POST">
+
+<table cellspacing="0" cellpadding="2" border="0">
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Id
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="id" size="40" />
+    </td>
+  </tr>
+
+  <tr>
+    <td align="left" valign="top">
+    </td>
+    <td align="left" valign="top">
+    <div class="form-element">
+    <input class="form-element" type="submit" name="submit" 
+     value="Add" /> 
+    </div>
+    </td>
+  </tr>
+</table>
+</form>
+
+<dtml-var manage_page_footer>
diff --git a/dtml/block_icon.gif b/dtml/block_icon.gif
new file mode 100644 (file)
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 (file)
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 (executable)
index 0000000..f1021cb
--- /dev/null
@@ -0,0 +1,299 @@
+<dtml-if management_page_charset>
+    <dtml-comment>
+    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.
+    </dtml-comment>
+    <dtml-call "REQUEST.set('management_page_charset',
+                          _['management_page_charset'])">
+    <dtml-call "REQUEST.set('management_page_charset_tag','')">
+<dtml-else>
+    <dtml-comment>
+    Thankfully no site-global encoding specification in a property.
+    We can set UTF-8, and unicode properties will work.
+    </dtml-comment>
+    <dtml-call "REQUEST.set('management_page_charset','UTF-8')">
+    <dtml-call "REQUEST.set('management_page_charset_tag','UTF-8:')">
+</dtml-if>
+    
+<dtml-if "REQUEST.get('management_page_charset',None)=='UTF-8'">
+<dtml-var "u' '">
+</dtml-if>
+<dtml-var manage_page_header>
+<dtml-with "_(management_view='Properties')">
+<dtml-var manage_tabs>
+</dtml-with>
+
+<dtml-if Principia-Version>
+<p>
+<em>You are currently working in version &dtml-Principia-Version;</em>
+</p>
+</dtml-if Principia-Version>
+
+<form action="&dtml-URL1;" method="post">
+<dtml-if propertyMap>
+<p class="form-help">
+Properties allow you to assign simple values to Zope objects. To change 
+property values, edit the values and click &quot;Save Changes&quot;. 
+</p>
+
+<table cellspacing="0" cellpadding="2" border="0">
+<tr class="list-header">
+  <td align="left" valign="top" width="16">
+  &nbsp;
+  </td>
+  <td align="left" valign="top">
+  <div class="form-label">
+  Name
+  </div>
+  </td>
+  <td align="left" valign="top">
+  <div class="form-label">
+  Value
+  </div>
+  </td>
+  <td align="left" valign="top">
+  <div class="form-label">
+  Type
+  </div>
+  </td>
+</tr>
+
+<dtml-in propertyMap mapping>
+<dtml-let type="not _.has_key('type') and 'string' or type">
+<tr>
+  <td align="left" valign="top" width="16">
+  <dtml-if "'d' in _['sequence-item'].get('mode', 'awd')">
+  <input type="checkbox" name="_ids:<dtml-var "REQUEST['management_page_charset_tag']">string:list" value="&dtml-id;"
+   id="cb-&dtml-id;">
+  <dtml-else>
+  </dtml-if>
+  </td>
+  <td align="left" valign="top">
+  <div class="form-label">
+  <label for="cb-&dtml-id;"><dtml-var "propertyLabel(id)" html_quote></label>
+  </div>
+  </td>
+  <td align="left" valign="top">
+
+  <dtml-if "'w' in _['sequence-item'].get('mode', 'awd')">
+  <dtml-if "type == 'int'">
+  <input type="text" name="&dtml-id;:&dtml-type;"
+   size="35" value="<dtml-if "hasProperty(id)"><dtml-var "'%s' % getProperty(id)" html_quote></dtml-if>">
+  <dtml-elif "type == 'long'">
+  <input type="text" name="&dtml-id;:&dtml-type;" size="35"
+   value="<dtml-if "hasProperty(id)"><dtml-var "('%s' % getProperty(id))" html_quote></dtml-if>">
+  <dtml-elif "type in ('float', 'date')">
+  <input type="text" name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">&dtml-type;" size="35"
+   value="<dtml-var "getProperty(id)" html_quote>">
+  <dtml-elif "type in ['string','ustring']">
+  <input type="text" name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">&dtml-type;" size="35"
+   value="<dtml-var "getProperty(id)" html_quote>">
+  <dtml-elif "type=='boolean'">
+  <input type="checkbox" name="&dtml-id;:boolean" size="35"
+   <dtml-if "getProperty(id)">CHECKED</dtml-if>>
+  <dtml-elif "type in ['tokens','utokens']">
+  <input type="text" name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">&dtml-type;" size="35"
+   value="<dtml-in "getProperty(id)">&dtml-sequence-item; </dtml-in>">
+  <dtml-elif "type in ['text','utext']">
+  <textarea name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">&dtml-type;" rows="6" cols="35"><dtml-var "getProperty(id)" html_quote></textarea>
+  <dtml-elif "type in ['lines','ulines']">
+  <textarea name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">&dtml-type;" rows="6" cols="35"><dtml-in
+  "getProperty(id)">&dtml-sequence-item;<dtml-if
+   sequence-end><dtml-else><dtml-var "'\n'"></dtml-if></dtml-in></textarea>
+
+  <dtml-elif "type=='selection'">
+
+    <dtml-if "hasProperty(select_variable)">
+    <div class="form-element">
+    <select name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">text">
+      <dtml-in "getProperty(select_variable)">
+         <option
+          <dtml-if "_['sequence-item']==getProperty(id)">SELECTED</dtml-if>
+          >&dtml-sequence-item;</option>
+      </dtml-in>
+    </select>
+    </div>
+    <dtml-elif "_.has_key(select_variable)">
+    <div class="form-element">
+    <select name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">text">
+      <dtml-in "_[select_variable]">
+         <option
+          <dtml-if "_['sequence-item']==getProperty(id)">SELECTED</dtml-if>
+          >&dtml-sequence-item;</option>
+      </dtml-in>
+    </select>
+    </div>
+    <dtml-else>
+    <div class="form-text">
+       No value for &dtml-select_variable;.
+    </div>
+    </dtml-if>
+
+  <dtml-elif "type=='multiple selection'">
+
+    <dtml-if "hasProperty(select_variable)">
+    <div class="form-element">
+    <select name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">list:string" multiple
+      size="<dtml-var "_.min(7, _.len(getProperty(select_variable)))">">
+      <dtml-in "getProperty(select_variable)">
+         <option<dtml-if 
+         "getProperty(id) and (_['sequence-item'] in getProperty(id))"
+         > SELECTED</dtml-if
+         >>&dtml-sequence-item;</option>
+      </dtml-in>
+    </select>
+    </div>
+    <dtml-elif "_.has_key(select_variable)">
+    <div class="form-element">
+    <select name="&dtml-id;:<dtml-var "REQUEST['management_page_charset_tag']">list:string" multiple
+      size="<dtml-var "_.min(7, _.len(_[select_variable]))">">
+      <dtml-in "_[select_variable]">
+         <option<dtml-if 
+         "getProperty(id) and (_['sequence-item'] in getProperty(id))"
+         > SELECTED</dtml-if
+         >>&dtml-sequence-item;</option>
+      </dtml-in>
+    </select>
+    </div>
+    <dtml-else>
+    <div class="form-text">
+    No value for &dtml-select_variable;.
+    </div>
+    </dtml-if>
+  <dtml-elif "type=='expr'">
+    <input type="text" name="<dtml-var id>:<dtml-var "REQUEST['management_page_charset_tag']"><dtml-var type>" size="35"
+     value="<dtml-var "getProperty(id).text" html_quote>">
+  <dtml-else>
+  <em>Unknown property type</em>
+  </dtml-if>
+  <dtml-else>
+  <table border="1">
+  <tr><td><dtml-var "getProperty(id)" html_quote></td></tr>
+  </table>
+  </dtml-if>
+  </td>
+  <td align="left" valign="top">
+  <div class="list-item">
+  &dtml-type;
+  </div>
+  </td>
+  
+  <dtml-if "id=='title'">
+    <td align="center" valign="top">
+      <div class="list-item"><b>Warning:</b> be aware that removing 'title' without re-adding it might be dangerous.</div>
+   </td>
+  </dtml-if>
+</tr>
+</dtml-let>
+</dtml-in>
+<tr>
+  <td colspan="2">&nbsp;</td>
+  <td align="left" valign="top">
+  <div class="form-element">
+  <input name="manage_editProperties:method" type="submit" 
+   class="form-element" value="Save Changes" />
+<dtml-if property_extensible_schema__>
+  <input name="manage_delProperties:method" type="submit" 
+   class="form-element" value="Delete" />
+  </div>
+  </td>
+  <td>
+  <dtml-comment>
+  This needs some community review before exposing it officially.
+  <input type="submit" name="manage_propertyTypeForm:method" value="Change Names/Types">
+  </dtml-comment>
+  </td>
+<dtml-else>
+  <td>&nbsp;</td>
+</dtml-if>
+  </td>
+</tr>
+</table>
+
+<dtml-else>
+<p class="form-help">
+Properties allow you to assign simple values to Zope objects. There are 
+currently no properties defined for this item. <dtml-if 
+property_extensible_schema__>To add a property, enter a name, type 
+and value and click the &quot;Add&quot; button.
+</dtml-if>
+</p>
+
+</dtml-if>
+</form>
+
+<dtml-if property_extensible_schema__>
+
+<form action="&dtml-URL1;/manage_addProperty" method="post">
+
+<p class="form-help">
+To add a new property, enter a name, type and value for the new 
+property and click the &quot;Add&quot; button. 
+</p>
+
+<table>
+<tr>
+  <td align="left" valign="top">
+  <div class="form-label">
+  Name
+  </div>
+  </td>
+  <td align="left" valign="top">
+  <input type="text" name="id:<dtml-var "REQUEST['management_page_charset_tag']">string" size="30" value=""/>
+  </td>
+  <td align="left" valign="top" class="form-label">
+  Type
+  </td>
+  <td align="left" valign="top">
+  <div class="form-element">
+    <select name="type">
+      <option>boolean</option>
+      <option>date</option>
+      <option>float</option>
+      <option>int</option>
+      <option>lines</option>
+      <option>long</option>
+      <option>expr</option>
+      <option selected>string</option>
+      <dtml-if "REQUEST['management_page_charset'] == 'UTF-8'">
+        <option>ustring</option>
+        <option>text</option>
+        <option>tokens</option>
+        <option>utext</option>
+        <option>utokens</option>
+        <option>ulines</option>
+      </dtml-if>
+      <option>selection</option>
+      <option>multiple selection</option>
+    </select>
+  </div>
+  </td>
+</tr>
+<tr>
+  <td align="left" valign="top">
+  <div class="form-label">
+  Value
+  </div>
+  </td>
+  <td colspan=2 align="left" valign="top">
+  <dtml-if "REQUEST['management_page_charset'] == 'UTF-8'">
+    <input type="text" name="value:UTF-8:ustring" size="30" />   
+  <dtml-else>   
+     <input type="text" name="value:string" size="30" />   
+  </dtml-if> 
+  </td>
+  <td align="right" valign="top">
+  <div class="form-element">
+  <input class="form-element" type="submit" name="submit" value=" Add " />
+  </div>
+  </td>
+</tr>
+</table>
+</form>
+</dtml-if>
+
+<dtml-var manage_page_footer>
+
+
diff --git a/dtml/rulesForm.dtml b/dtml/rulesForm.dtml
new file mode 100755 (executable)
index 0000000..12d2e1b
--- /dev/null
@@ -0,0 +1,173 @@
+<dtml-let form_title="'Actions'">
+<dtml-if manage_page_header>
+ <dtml-var manage_page_header>
+<dtml-else>
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+ <html lang="en">
+ <head>
+ <title>&dtml-form_title;</title>
+ </head>
+ <body bgcolor="#FFFFFF" link="#000099" vlink="#555555">
+ <h3>&dtml-form_title;</h3>
+</dtml-if>
+</dtml-let>
+<dtml-var manage_tabs>
+
+<dtml-let mbis="[ct.id for ct in mosaic_tool.listTypeInfo() if ct.meta_type == 'Mosaic Block Information']"
+          availMbis="[ct.id for ct in mosaic_tool.listTypeInfo() if ct.meta_type == 'Mosaic Block Information' and ct.id not in objectIds(['Rule Information',])]">
+<dtml-if "objectIds(['Rule Information',])">
+<form action="&dtml-absolute_url;" method="POST">
+<dtml-in "objectValues(['Rule Information',])">
+<dtml-let currentSlot="_['sequence-item']">
+  <table>
+    <tr>
+      <td>
+        <input type="checkbox" name="ruleSelection:list"
+               value="<dtml-var "_['sequence-item'].id">">
+      </td>
+      <td class="form-label">
+        Block type
+      </td>
+      <td>
+        <dtml-let blockId="_['sequence-item'].id">
+        <select name="<dtml-var "blockId+'.id:record'">">
+          <dtml-in "[blockId,] + availMbis">
+            <option value="<dtml-var sequence-item>" <dtml-if "blockId == _['sequence-item']">selected</dtml-if>>
+              <dtml-var sequence-item>
+            </option>
+          </dtml-in>
+        </select>
+        </dtml-let>
+      </td>
+    </tr>
+    <tr>
+      <td></td>
+      <td class="form-label">
+        Max intances nb
+      </td>
+      <td>
+        <input type="text"
+               name="<dtml-var "_['sequence-item'].id + '.maxNbInstance:int:record'">"
+               value="<dtml-var "_['sequence-item'].maxNbInstance">">
+      </td>
+    </tr>
+    <tr>
+      <td></td>
+      <td class="form-label">
+        Allow Move
+      </td>
+      <td>
+       <select name="<dtml-var "_['sequence-item'].id + '.allowMove:int:record'">">
+          <option value="1" <dtml-if "_['sequence-item'].allowMove">selected</dtml-if>>yes</option>
+          <option value="0" <dtml-if "not _['sequence-item'].allowMove">selected</dtml-if>>no</option>
+        </select>
+      </td>
+    </tr>
+    <tr>
+      <td></td>
+      <td class="form-label">
+        Allow Move right and left
+      </td>
+      <td>
+       <select name="<dtml-var "_['sequence-item'].id + '.allowMoveRightAndLeft:int:record'">">
+          <option value="1" <dtml-if "_['sequence-item'].allowMoveRightAndLeft">selected</dtml-if>>yes</option>
+          <option value="0" <dtml-if "not _['sequence-item'].allowMoveRightAndLeft">selected</dtml-if>>no</option>
+        </select>
+      </td>
+    </tr>
+    <tr>
+      <td></td>
+      <td class="form-label">
+        Allow Move up and down
+      </td>
+      <td>
+       <select name="<dtml-var "_['sequence-item'].id + '.allowMoveUpAndDown:int:record'">">
+          <option value="1" <dtml-if "_['sequence-item'].allowMoveUpAndDown">selected</dtml-if>>yes</option>
+          <option value="0" <dtml-if "not _['sequence-item'].allowMoveUpAndDown">selected</dtml-if>>no</option>
+        </select>
+      </td>
+    </tr>
+    <tr>
+      <td></td>
+      <td class="form-label">
+        Mode
+      </td>
+      <td>
+        <select name="<dtml-var "_['sequence-item'].id + '.mode:record'">"
+          <option value="wd" <dtml-if "_['sequence-item'].mode == 'wd'">selected</dtml-if>>wd</option>
+          <option value="w" <dtml-if "_['sequence-item'].mode == 'w'">selected</dtml-if>>w</option>
+        </select>
+      </td>
+    </tr>
+    <tr>
+      <td colspan="3"><hr></td>
+    </tr>
+  </table>
+</dtml-let>
+</dtml-in>
+<div>
+  <input type="submit" name="saveRules:method" value="Save">
+  <input type="submit" name="deleteRules:method" value="Delete">
+</div>
+</form>
+</dtml-if>
+
+<dtml-if availMbis>
+<h3 class="form-help">Add a rule</h3>
+<form action="manage_addRule" method="POST">
+<table>
+  <tr>
+    <td class="form-label">
+      Block type
+    </td>
+    <td class="form-label">
+      <select name="id">
+        <dtml-in availMbis>
+          <option value="<dtml-var sequence-item>"><dtml-var sequence-item></option>
+        </dtml-in>
+      </select>
+    </td>
+  </tr>
+  <tr>
+    <td class="form-label">
+       Max intances nb
+    </td>
+    <td class="form-label">
+      <input type="text" name="maxNbInstance:int" value="0">
+    </td>
+  </tr>
+  <tr>
+  <tr>
+    <td class="form-label">
+      Allow Move
+    </td>
+    <td>
+      <select name="allowMove:int">
+        <option value="1" selected>yes</option>
+        <option value="0">no</option>
+      </select>
+  </td>
+  </tr>
+  <tr>
+    <td class="form-label">
+      Mode
+    </td>
+    <td>
+      <select name="mode">"
+        <option value="wd" selected>wd</option>
+        <option value="w">w</option>
+      </select>
+    </td>
+  </tr>
+  </tr>
+    <td colspan="2"><input type="submit" value="Add"></td>
+  </tr>
+</table>
+</form>
+</dtml-if>
+</dtml-let>
+<dtml-if manage_page_footer>
+  <dtml-var manage_page_footer>
+<dtml-else>
+  </body></html>
+</dtml-if>
diff --git a/dtml/slot_icon.gif b/dtml/slot_icon.gif
new file mode 100644 (file)
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 (executable)
index 0000000..f67115f
--- /dev/null
@@ -0,0 +1,119 @@
+<dtml-let form_title="'Actions'">
+<dtml-if manage_page_header>
+ <dtml-var manage_page_header>
+<dtml-else>
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+ <html lang="en">
+ <head>
+ <title>&dtml-form_title;</title>
+ </head>
+ <body bgcolor="#FFFFFF" link="#000099" vlink="#555555">
+ <h3>&dtml-form_title;</h3>
+</dtml-if>
+</dtml-let>
+<dtml-var manage_tabs>
+<dtml-if "objectIds(['Slot Information',])">
+<form action="&dtml-absolute_url;" method="POST">
+<dtml-in "objectValues(['Slot Information',])">
+<dtml-let currentSlot="_['sequence-item']">
+  <table>
+    <tr>
+      <td>
+        <input type="checkbox" name="slotSelection:list"
+               value="<dtml-var "_['sequence-item'].id">">
+      </td>
+      <td class="form-label">
+        Id
+      </td>
+      <td>
+        <input type="text"
+               name="<dtml-var "_['sequence-item'].id + '.id:record'">"
+               value="<dtml-var "_['sequence-item'].id">">
+      </td>
+    </tr>
+    <tr>
+      <td></td>
+      <td class="form-label">
+        Type
+      </td>
+      <td>
+        <input type="text"
+               name="<dtml-var "_['sequence-item'].id + '.type:record'">"
+               value="<dtml-var "_['sequence-item'].type">">
+      </td>
+    </tr>
+    <tr>
+      <td></td>
+      <td class="form-label" valign="top">
+        Arguments
+      </td>
+      <td>
+        <table border="1">
+          <tr>
+            <th>Id</th>
+            <th>Type</th>
+            <th>Value</th>
+          </tr>
+          <dtml-in "_['sequence-item'].propertyMap()">
+          <tr>
+            <td><dtml-var "_['sequence-item']['id']"></td>
+            <td><dtml-var "_['sequence-item']['type']"></td>
+            <td><dtml-var "currentSlot.getProperty(_['sequence-item']['id'])"></td>
+          </tr>
+          </dtml-in>
+          <tr>
+            <td colspan="3">
+            <div class="std-text">
+              <a href="<dtml-var "currentSlot.absolute_url() + '/manage_propertiesForm'">">
+                Edit arguments...
+              </a>
+            </div>
+            </td>
+          </tr>
+        </table>
+      </td>
+    </tr>
+
+    <tr>
+      <td colspan="3"><hr></td>
+    </tr>
+  </table>
+</dtml-let>
+</dtml-in>
+<div>
+  <input type="submit" name="saveSlots:method" value="Save">
+  <input type="submit" name="deleteSlots:method" value="Delete">
+</div>
+</form>
+</dtml-if>
+<h3 class="form-help">Add a slot</h3>
+<form action="manage_addSlot" method="POST">
+<table>
+  <tr>
+    <td class="form-label">
+      Id
+    </td>
+    <td class="form-label">
+      <input type="text" name="id">
+    </td>
+  </tr>
+  <tr>
+    <td class="form-label">
+      Type
+    </td>
+    <td class="form-label">
+      <input type="text" name="type">
+    </td>
+  </tr>
+  <tr>
+    <td colspan="2">
+      <input type="submit" value="Add">
+    </td>
+  </tr>
+</table>
+</form>
+<dtml-if manage_page_footer>
+  <dtml-var manage_page_footer>
+<dtml-else>
+  </body></html>
+</dtml-if>
diff --git a/exportimport/__init__.py b/exportimport/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/exportimport/mosaictool.py b/exportimport/mosaictool.py
new file mode 100644 (file)
index 0000000..a0a55e1
--- /dev/null
@@ -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 (executable)
index 0000000..3912109
--- /dev/null
@@ -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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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 (executable)
index 0000000..136d063
--- /dev/null
@@ -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 (executable)
index 0000000..2b1776e
--- /dev/null
@@ -0,0 +1,71 @@
+<html>
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+    <title>block_canvas</title>
+  </head>
+
+  <body tal:define="baseBlock nocall:block;
+                    tableInfo baseBlock/getBlocksTable;
+                    rows tableInfo/rows;
+                    cols tableInfo/cols;
+                    lines tableInfo/lines;
+                    allowedBlocks python:(displayAction != 'edit' and [(),] or [baseBlock.getAllowedBlocks()])[0];"
+        tal:omit-tag="" i18n:domain="plinn" metal:define-macro="canvas">
+      <table width="100%">
+         
+        <tr tal:condition="python:not rows  and displayAction == 'edit'">
+          <td>
+            <form metal:use-macro="here/block_utils/macros/add_block_form">Formulaire de création du premier block</form>
+          </td>
+        </tr>
+         
+           <tr tal:repeat="line lines">
+             <span tal:repeat="blockInfo line" tal:omit-tag="">
+               <td valign="top" tal:condition="python:blockInfo.get('rowspan', 1) > 0"
+                   tal:attributes="rowspan python:blockInfo.get('rowspan', 1)"
+                   tal:define="block blockInfo/block">
+                 <div tal:condition="block" tal:omit-tag="">
+                 
+                <div tal:condition="python:displayAction == 'edit'" tal:omit-tag="">
+                  <form metal:use-macro="here/block_utils/macros/add_block_form">Formulaire de création d'un block au dessus
+                    <span metal:fill-slot="additional_fields" tal:omit-tag="">
+                      <input type="hidden" name="xpos:int" value="0" tal:attributes="value blockInfo/col" />
+                      <input type="hidden" name="beforeBlock" value="hiddenValue" tal:attributes="value block/getId|nothing" />
+                    </span>
+                  </form>
+                </div>
+                 
+                <a name="anchor" tal:attributes="name block/getId"></a>
+                <div tal:condition="python:displayAction == 'edit'" tal:omit-tag="">
+                  <span metal:use-macro="here/block_utils/macros/move_table">tableau des boutons de délacement</span>
+                </div>
+                <div tal:replace="structure python:block.callTemplate(displayAction=displayAction, baseBlock=baseBlock, blockInfo=blockInfo, here=here)">
+                  <h2>Rendu HTML du block</h2>
+                </div>
+                <div tal:condition="python:displayAction == 'edit' and blockInfo.get('lastOne')"
+                     tal:omit-tag="">
+                  <form metal:use-macro="here/block_utils/macros/add_block_form">Formulaire de création d'un block au dessus
+                       <span tal:omit-tag="" metal:fill-slot="additional_fields">
+                     <input type="hidden" name="xpos:int" value="0" tal:attributes="value blockInfo/col" />
+                     <input type="hidden" name="afterBlock" value="hiddenValue" tal:attributes="value block/getId|nothing" />
+                    </span>
+                  </form>
+                </div>
+                 </div>
+                 
+            <div tal:condition="python:not block and displayAction == 'edit'">
+              <form metal:use-macro="here/block_utils/macros/add_block_form">Formulaire de création du premier block
+                <span tal:omit-tag="" metal:fill-slot="additional_fields">
+                  <input type="hidden" name="xpos:int" value="0" tal:attributes="value blockInfo/col" />
+                </span>
+              </form>
+            </div>
+
+               </td>
+             </span>
+           </tr>
+         </table>
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/skins/collapse_all.ai b/skins/collapse_all.ai
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..0f68fff
--- /dev/null
@@ -0,0 +1,5 @@
+<script type="text/javascript">
+//<!--
+new TreeMaker(document.getElementById("&dtml-box_id;"), "&dtml-filter_str;", portal_url()+'/');
+//-->
+</script>
diff --git a/skins/make_nav_tree.py b/skins/make_nav_tree.py
new file mode 100755 (executable)
index 0000000..a6b02c9
--- /dev/null
@@ -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 (file)
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 (file)
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 (executable)
index 0000000..e47016c
--- /dev/null
@@ -0,0 +1,18 @@
+<html xmlns="http://www.w3.org/1999/xhtml" metal:use-macro="here/main_template/macros/master">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  </head>
+
+  <body i18n:domain="plinn">
+    <div metal:fill-slot="header">
+      <div metal:use-macro="here/widgets/macros/generic_header">
+        generic_header macro</div>
+    </div>
+    <div tal:define="block nocall:here ; displayAction string:edit" tal:omit-tag="" metal:fill-slot="main">
+      <div><a title="Paste" href="." tal:attributes="href string:${block/absolute_url}/pasteBlocks" tal:condition="python:here.cb_dataValid() and block.haveRules()" i18n:attributes="title"><img src="paste.png" alt="Paste" height="14" width="16" tal:attributes="src string:paste.png" border="0" i18n:attributes="alt" /></a></div>
+      <div metal:use-macro="here/block_canvas/macros/canvas">Block Canvas</div>
+    </div>
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/skins/mosaicdocument_view.pt b/skins/mosaicdocument_view.pt
new file mode 100755 (executable)
index 0000000..7ccc35a
--- /dev/null
@@ -0,0 +1,19 @@
+<html metal:use-macro="here/main_template_view/macros/master" xmlns="http://www.w3.org/1999/xhtml">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=iso-8859-1" />
+    <title>Visualisation d'un document mosa&iuml;que</title>
+  </head>
+
+  <body>
+    <div metal:fill-slot="header">
+      <div metal:use-macro="here/widgets/macros/generic_header">
+        generic_header macro</div>
+    </div>
+    <div metal:fill-slot="main" tal:omit-tag="" tal:define="block nocall:here;
+                    displayAction string:view">
+      <div metal:use-macro="here/block_canvas/macros/canvas">Block Canvas here</div>
+    </div>
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/skins/paste.png b/skins/paste.png
new file mode 100644 (file)
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 (file)
index 0000000..30013fb
--- /dev/null
@@ -0,0 +1,36 @@
+##parameters=filter=''
+
+def replaceXMLEntities(text) :
+       for c, ent in (('<', '&lt;'), ('>', '&gt;'), ('&', '&amp;')) :
+               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 '<xml>'
+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 = '<row id="%(id)s" url="%(url)s" icon="%(icon)s" height="%(height)d" width="%(width)d" state="%(state)s" description="%(description)s">%(title)s</row>' % {
+                 '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 '</xml>'
+
+return printed
\ No newline at end of file
diff --git a/tool.gif b/tool.gif
new file mode 100644 (file)
index 0000000..a680450
Binary files /dev/null and b/tool.gif differ
diff --git a/version.txt b/version.txt
new file mode 100755 (executable)
index 0000000..9f8e9b6
--- /dev/null
@@ -0,0 +1 @@
+1.0
\ No newline at end of file