style thème sombre.
[Plinn.git] / Folder.py
index fd7609b..8ed3cc3 100644 (file)
--- a/Folder.py
+++ b/Folder.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #######################################################################################
 #   Plinn - http://plinn.org                                                          #
-#   Copyright (C) 2005-2007  Benoît PIN <benoit.pin@ensmp.fr>                         #
+#   Copyright (C) 2005-2014  Benoît Pin <benoit.pin@ensmp.fr>                         #
 #                                                                                     #
 #   This program is free software; you can redistribute it and/or                     #
 #   modify it under the terms of the GNU General Public License                       #
 """
 
 from OFS.CopySupport import CopyError, eNoData, _cb_decode, eInvalid, eNotFound,\
-                                                       eNotSupported, sanity_check, cookie_path
+                            eNotSupported, sanity_check, cookie_path
 from App.Dialogs import MessageDialog
 from zExceptions import BadRequest
+from zExceptions import Unauthorized
 import sys
 import warnings
 from cgi import escape
+from urllib import unquote
 from OFS import Moniker
 from ZODB.POSException import ConflictError
 import OFS.subscribers
 from zope.event import notify
 from zope.lifecycleevent import ObjectCopiedEvent
-from zope.app.container.contained import ObjectMovedEvent
-from zope.app.container.contained import notifyContainerModified
+try :
+    from zope.app.container.contained import notifyContainerModified
+    from zope.app.container.contained import ObjectMovedEvent
+except ImportError :
+    ## Zope-2.13 compat
+    from zope.container.contained import notifyContainerModified
+    from zope.container.contained import ObjectMovedEvent
 from OFS.event import ObjectClonedEvent
 from OFS.event import ObjectWillBeMovedEvent
 from zope.component.factory import Factory
 from Acquisition import aq_base, aq_inner, aq_parent
 
-from types import StringType
+from types import StringType, NoneType
 from Products.CMFCore.permissions import ListFolderContents, View, ViewManagementScreens,\
-                                                                                ManageProperties, AddPortalFolders, AddPortalContent,\
-                                                                                ManagePortal, ModifyPortalContent
+                                         ManageProperties, AddPortalFolders, AddPortalContent,\
+                                         ManagePortal, ModifyPortalContent
 from permissions import DeletePortalContents, DeleteObjects, DeleteOwnedObjects, SetLocalRoles, CheckMemberPermission
 from Products.CMFCore.utils import _checkPermission, getToolByName
+from Products.CMFCore.utils import getUtilityByInterfaceName
 from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
 from Products.CMFCore.PortalFolder import PortalFolder, ContentFilter
+from Products.CMFCore.interfaces import IDublinCore
 from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
 
 from zope.interface import implements
@@ -57,410 +66,492 @@ from Products.CMFCore.interfaces import IContentish
 
 from utils import _checkMemberPermission
 from utils import Message as _
+from utils import makeValidId
 from Globals import InitializeClass
 from AccessControl import ClassSecurityInfo
+from ZServer import LARGE_FILE_THRESHOLD
+from webdav.interfaces import IWriteLock
+from webdav.common import Locked
+from webdav.common import PreconditionFailed
+from zope.contenttype import guess_content_type
 
 
 class PlinnFolder(CMFCatalogAware, PortalFolder, DefaultDublinCoreImpl) :
-       """ Plinn Folder """
-       
-       implements(IContentish)
-
-       security = ClassSecurityInfo()
-       
-       manage_options = PortalFolder.manage_options
-
-       ## change security for inherited methods
-       security.declareProtected(AddPortalContent, 'manage_pasteObjects')
-
-       def __init__( self, id, title='' ) :
-               PortalFolder.__init__(self, id)
-               DefaultDublinCoreImpl.__init__(self, title = title)
-               
-       security.declarePublic('allowedContentTypes')
-       def allowedContentTypes(self):
-               """
-               List type info objects for types which can be added in this folder.
-               Types can be filtered using the localContentTypes attribute.
-               """
-               allowedTypes = PortalFolder.allowedContentTypes(self)
-               if hasattr(self, 'localContentTypes'):
-                       allowedTypes = [t for t in allowedTypes if t.title in self.localContentTypes]
-               return allowedTypes
-
-       security.declareProtected(View, 'objectIdCanBeDeleted')
-       def objectIdCanBeDeleted(self, id) :
-               """ Check permissions and ownership and return True
-                       if current user can delete object id.
-               """
-               if _checkPermission(DeleteObjects, self) : # std zope perm
-                       return True
-
-               elif _checkPermission(DeletePortalContents, self):
-                       mtool = getToolByName(self, 'portal_membership')
-                       authMember = mtool.getAuthenticatedMember()
-                       ob = getattr(self, id)
-                       if authMember.allowed(ob, object_roles=['Owner'] ) and \
-                          _checkPermission(DeleteOwnedObjects, ob) : return True
-
-               else :
-                       return False
-
-
-       security.declareProtected(DeletePortalContents, 'manage_delObjects')
-       def manage_delObjects(self, ids=[], REQUEST=None):
-               """Delete subordinate objects.
-                  A member can delete his owned contents (if he has the 'Delete Portal Contents' permission)
-                  without 'Delete objects' permission in this folder.
-                  Return skipped object ids.
-               """
-               notOwned = []
-               if _checkPermission(DeleteObjects, self) : # std zope perm
-                       PortalFolder.manage_delObjects(self, ids=ids, REQUEST=REQUEST)
-               else :
-                       mtool = getToolByName(self, 'portal_membership')
-                       authMember = mtool.getAuthenticatedMember()
-                       owned = []
-                       if type(ids) == StringType :
-                               ids = [ids]
-                       for id in ids :
-                               ob = self._getOb(id)
-                               if authMember.allowed(ob, object_roles=['Owner'] ) and \
-                                  _checkPermission(DeleteOwnedObjects, ob) : owned.append(id)
-                               else : notOwned.append(id)
-                       if owned :
-                               PortalFolder.manage_delObjects(self, ids=owned, REQUEST=REQUEST)
-
-               if REQUEST is not None:
-                       return self.manage_main(
-                               self, REQUEST,
-                               manage_tabs_message='Object(s) deleted.',
-                               update_menu=1)
-               return notOwned
-
-
-       security.declareProtected(AddPortalContent, 'manage_renameObjects')
-       def manage_renameObjects(self, ids=[], new_ids=[], REQUEST=None) :
-               """ Rename subordinate objects
-                       A member can rename his owned contents if he has the 'Modify Portal Content' permission.
-                       Returns skippend object ids.
-               """
-               if len(ids) != len(new_ids):
-                       raise BadRequest(_('Please rename each listed object.'))
-               
-               if _checkPermission(ViewManagementScreens, self) : # std zope perm
-                       return super(PlinnFolder, self).manage_renameObjects(ids, new_ids, REQUEST)
-                       
-               mtool = getToolByName(self, 'portal_membership')
-               authMember = mtool.getAuthenticatedMember()
-               skiped = []
-               for id, new_id in zip(ids, new_ids) :
-                       if id == new_id : continue
-                       
-                       ob = self._getOb(id)
-                       if authMember.allowed(ob, object_roles=['Owner'] ) and \
-                          _checkPermission(ModifyPortalContent, ob) :
-                               self.manage_renameObject(id, new_id)
-                       else :
-                               skiped.append(id)
-               
-               if REQUEST is not None :
-                       return self.manage_main(self, REQUEST, update_menu=1)
-
-               return skiped
-
-
-       security.declareProtected(ListFolderContents, 'listFolderContents')
-       def listFolderContents( self, contentFilter=None ):
-               """ List viewable contentish and folderish sub-objects.
-               """
-               items = self.contentItems(filter=contentFilter)
-               l = []
-               for id, obj in items:
-                       if _checkPermission(View, obj) :
-                               l.append(obj)
-
-               return l
-
-
-       security.declareProtected(ListFolderContents, 'listNearestFolderContents')
-       def listNearestFolderContents(self, contentFilter=None, userid=None, sorted=False) :
-               """ Return folder contents and traverse
-               recursively unaccessfull sub folders to find
-               accessible contents.
-               """
-
-               filt = {}
-               if contentFilter :
-                       filt = contentFilter.copy()
-               ctool = getToolByName(self, 'portal_catalog')
-               mtool = getToolByName(self, 'portal_membership')
-
-               if userid and _checkPermission(CheckMemberPermission, getToolByName(self, 'portal_url').getPortalObject()) :
-                       checkFunc = lambda perm, ob : _checkMemberPermission(userid, View, ob)
-                       filt['allowedRolesAndUsers'] = ctool._listAllowedRolesAndUsers( mtool.getMemberById(userid) )
-               else :
-                       checkFunc = _checkPermission
-                       filt['allowedRolesAndUsers'] = ctool._listAllowedRolesAndUsers( mtool.getAuthenticatedMember() )
-               
-               
-               # copy from CMFCore.PortalFolder.PortalFolder._filteredItems
-               pt = filt.get('portal_type', [])
-               if type(pt) is type(''):
-                       pt = [pt]
-               types_tool = getToolByName(self, 'portal_types')
-               allowed_types = types_tool.listContentTypes()
-               if not pt:
-                       pt = allowed_types
-               else:
-                       pt = [t for t in pt if t in allowed_types]
-               if not pt:
-                       # After filtering, no types remain, so nothing should be
-                       # returned.
-                       return []
-               filt['portal_type'] = pt
-               #---
-
-               query = ContentFilter(**filt)
-               nearestObjects = []
-               
-               for o in self.objectValues() :
-                       if query(o) :
-                               if checkFunc(View, o):
-                                       nearestObjects.append(o)
-                               elif getattr(o.aq_self,'isAnObjectManager', False):
-                                       nearestObjects.extend(_getDeepObjects(self, ctool, o, filter=filt))
-                               
-               if sorted and len(nearestObjects) > 0 :
-                       key, reverse = self.getDefaultSorting()
-                       if key != 'position' :
-                               indexCallable = callable(getattr(nearestObjects[0], key))
-                               if indexCallable :
-                                       sortfunc = lambda a, b : cmp(getattr(a, key)(), getattr(b, key)())
-                               else :                       
-                                       sortfunc = lambda a, b : cmp(getattr(a, key), getattr(b, key))
-                               nearestObjects.sort(cmp=sortfunc, reverse=reverse)
-               
-               return nearestObjects
-       
-       security.declareProtected(ListFolderContents, 'listCatalogedContents')
-       def listCatalogedContents(self, contentFilter={}):
-               """ query catalog and returns brains of contents.
-                       Requires ExtendedPathIndex
-               """
-               ctool = getToolByName(self, 'portal_catalog')
-               contentFilter['path'] = {'query':'/'.join(self.getPhysicalPath()),
-                                                               'depth':1}
-               return ctool(sort_on='position', **contentFilter)
-       
-
-       security.declarePublic('synContentValues')
-       def synContentValues(self):
-               # value for syndication
-               return self.listNearestFolderContents()
-
-       security.declareProtected(View, 'SearchableText')
-       def SearchableText(self) :
-               """ for full text indexation
-               """
-               return '%s %s' % (self.title, self.description)
-
-       security.declareProtected(AddPortalFolders, 'manage_addPlinnFolder')
-       def manage_addPlinnFolder(self, id, title='', REQUEST=None):
-               """Add a new PortalFolder object with id *id*.
-               """
-               ob=PlinnFolder(id, title)
-               # from CMFCore.PortalFolder.PortalFolder :-)
-               self._setObject(id, ob)
-               if REQUEST is not None:
-                       return self.folder_contents( # XXX: ick!
-                               self, REQUEST, portal_status_message="Folder added")
-
-       
-       ## overload to maintain ownership if authenticated user has 'Manage portal' permission
-       def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
-               """Paste previously copied objects into the current object.
-
-               If calling manage_pasteObjects from python code, pass the result of a
-               previous call to manage_cutObjects or manage_copyObjects as the first
-               argument.
-
-               Also sends IObjectCopiedEvent and IObjectClonedEvent
-               or IObjectWillBeMovedEvent and IObjectMovedEvent.
-               """
-               if cb_copy_data is not None:
-                       cp = cb_copy_data
-               elif REQUEST is not None and REQUEST.has_key('__cp'):
-                       cp = REQUEST['__cp']
-               else:
-                       cp = None
-               if cp is None:
-                       raise CopyError, eNoData
-
-               try:
-                       op, mdatas = _cb_decode(cp)
-               except:
-                       raise CopyError, eInvalid
-
-               oblist = []
-               app = self.getPhysicalRoot()
-               for mdata in mdatas:
-                       m = Moniker.loadMoniker(mdata)
-                       try:
-                               ob = m.bind(app)
-                       except ConflictError:
-                               raise
-                       except:
-                               raise CopyError, eNotFound
-                       self._verifyObjectPaste(ob, validate_src=op+1)
-                       oblist.append(ob)
-
-               result = []
-               if op == 0:
-                       # Copy operation
-                       mtool = getToolByName(self, 'portal_membership')
-                       utool = getToolByName(self, 'portal_url')
-                       portal = utool.getPortalObject()
-                       userIsPortalManager = mtool.checkPermission(ManagePortal, portal)
-
-                       for ob in oblist:
-                               orig_id = ob.getId()
-                               if not ob.cb_isCopyable():
-                                       raise CopyError, eNotSupported % escape(orig_id)
-
-                               try:
-                                       ob._notifyOfCopyTo(self, op=0)
-                               except ConflictError:
-                                       raise
-                               except:
-                                       raise CopyError, MessageDialog(
-                                               title="Copy Error",
-                                               message=sys.exc_info()[1],
-                                               action='manage_main')
-
-                               id = self._get_id(orig_id)
-                               result.append({'id': orig_id, 'new_id': id})
-
-                               orig_ob = ob
-                               ob = ob._getCopy(self)
-                               ob._setId(id)
-                               notify(ObjectCopiedEvent(ob, orig_ob))
-                               
-                               if not userIsPortalManager :
-                                       self._setObject(id, ob, suppress_events=True)
-                               else :
-                                       self._setObject(id, ob, suppress_events=True, set_owner=0)
-                               ob = self._getOb(id)
-                               ob.wl_clearLocks()
-
-                               ob._postCopy(self, op=0)
-
-                               OFS.subscribers.compatibilityCall('manage_afterClone', ob, ob)
-
-                               notify(ObjectClonedEvent(ob))
-
-                       if REQUEST is not None:
-                               return self.manage_main(self, REQUEST, update_menu=1,
-                                                                               cb_dataValid=1)
-
-               elif op == 1:
-                       # Move operation
-                       for ob in oblist:
-                               orig_id = ob.getId()
-                               if not ob.cb_isMoveable():
-                                       raise CopyError, eNotSupported % escape(orig_id)
-
-                               try:
-                                       ob._notifyOfCopyTo(self, op=1)
-                               except ConflictError:
-                                       raise
-                               except:
-                                       raise CopyError, MessageDialog(
-                                               title="Move Error",
-                                               message=sys.exc_info()[1],
-                                               action='manage_main')
-
-                               if not sanity_check(self, ob):
-                                       raise CopyError, "This object cannot be pasted into itself"
-
-                               orig_container = aq_parent(aq_inner(ob))
-                               if aq_base(orig_container) is aq_base(self):
-                                       id = orig_id
-                               else:
-                                       id = self._get_id(orig_id)
-                               result.append({'id': orig_id, 'new_id': id})
-
-                               notify(ObjectWillBeMovedEvent(ob, orig_container, orig_id,
-                                                                                         self, id))
-
-                               # try to make ownership explicit so that it gets carried
-                               # along to the new location if needed.
-                               ob.manage_changeOwnershipType(explicit=1)
-
-                               try:
-                                       orig_container._delObject(orig_id, suppress_events=True)
-                               except TypeError:
-                                       orig_container._delObject(orig_id)
-                                       warnings.warn(
-                                               "%s._delObject without suppress_events is discouraged."
-                                               % orig_container.__class__.__name__,
-                                               DeprecationWarning)
-                               ob = aq_base(ob)
-                               ob._setId(id)
-
-                               try:
-                                       self._setObject(id, ob, set_owner=0, suppress_events=True)
-                               except TypeError:
-                                       self._setObject(id, ob, set_owner=0)
-                                       warnings.warn(
-                                               "%s._setObject without suppress_events is discouraged."
-                                               % self.__class__.__name__, DeprecationWarning)
-                               ob = self._getOb(id)
-
-                               notify(ObjectMovedEvent(ob, orig_container, orig_id, self, id))
-                               notifyContainerModified(orig_container)
-                               if aq_base(orig_container) is not aq_base(self):
-                                       notifyContainerModified(self)
-
-                               ob._postCopy(self, op=1)
-                               # try to make ownership implicit if possible
-                               ob.manage_changeOwnershipType(explicit=0)
-
-                       if REQUEST is not None:
-                               REQUEST['RESPONSE'].setCookie('__cp', 'deleted',
-                                                                       path='%s' % cookie_path(REQUEST),
-                                                                       expires='Wed, 31-Dec-97 23:59:59 GMT')
-                               REQUEST['__cp'] = None
-                               return self.manage_main(self, REQUEST, update_menu=1,
-                                                                               cb_dataValid=0)
-
-               return result
-
-               
+    """ Plinn Folder """
+    
+    implements(IContentish)
+
+    security = ClassSecurityInfo()
+    
+    manage_options = PortalFolder.manage_options
+
+    ## change security for inherited methods
+    security.declareProtected(AddPortalContent, 'manage_pasteObjects')
+
+    def __init__( self, id, title='' ) :
+        PortalFolder.__init__(self, id)
+        DefaultDublinCoreImpl.__init__(self, title = title)
+            
+    security.declarePublic('allowedContentTypes')
+    def allowedContentTypes(self):
+        """
+        List type info objects for types which can be added in this folder.
+        Types can be filtered using the localContentTypes attribute.
+        """
+        allowedTypes = PortalFolder.allowedContentTypes(self)
+        if hasattr(self, 'localContentTypes'):
+            allowedTypes = [t for t in allowedTypes if t.title in self.localContentTypes]
+        return allowedTypes
+
+    security.declareProtected(View, 'objectIdCanBeDeleted')
+    def objectIdCanBeDeleted(self, id) :
+        """ Check permissions and ownership and return True
+            if current user can delete object id.
+        """
+        if _checkPermission(DeleteObjects, self) : # std zope perm
+            return True
+
+        elif _checkPermission(DeletePortalContents, self):
+            mtool = getToolByName(self, 'portal_membership')
+            authMember = mtool.getAuthenticatedMember()
+            ob = getattr(self, id)
+            if authMember.allowed(ob, object_roles=['Owner'] ) and \
+               _checkPermission(DeleteOwnedObjects, ob) : return True
+
+        else :
+            return False
+
+
+    security.declareProtected(DeletePortalContents, 'manage_delObjects')
+    def manage_delObjects(self, ids=[], REQUEST=None):
+        """Delete subordinate objects.
+           A member can delete his owned contents (if he has the 'Delete Portal Contents' permission)
+           without 'Delete objects' permission in this folder.
+           Return skipped object ids.
+        """
+        notOwned = []
+        if _checkPermission(DeleteObjects, self) : # std zope perm
+            PortalFolder.manage_delObjects(self, ids=ids, REQUEST=REQUEST)
+        else :
+            mtool = getToolByName(self, 'portal_membership')
+            authMember = mtool.getAuthenticatedMember()
+            owned = []
+            if type(ids) == StringType :
+                ids = [ids]
+            for id in ids :
+                ob = self._getOb(id)
+                if authMember.allowed(ob, object_roles=['Owner'] ) and \
+                   _checkPermission(DeleteOwnedObjects, ob) : owned.append(id)
+                else : notOwned.append(id)
+            if owned :
+                PortalFolder.manage_delObjects(self, ids=owned, REQUEST=REQUEST)
+
+        if REQUEST is not None:
+            return self.manage_main(
+                self, REQUEST,
+                manage_tabs_message='Object(s) deleted.',
+                update_menu=1)
+        return notOwned
+
+
+    security.declareProtected(AddPortalContent, 'manage_renameObjects')
+    def manage_renameObjects(self, ids=[], new_ids=[], REQUEST=None) :
+        """ Rename subordinate objects
+            A member can rename his owned contents if he has the 'Modify Portal Content' permission.
+            Returns skippend object ids.
+        """
+        if len(ids) != len(new_ids):
+            raise BadRequest(_('Please rename each listed object.'))
+        
+        if _checkPermission(ViewManagementScreens, self) : # std zope perm
+            return super(PlinnFolder, self).manage_renameObjects(ids, new_ids, REQUEST)
+            
+        mtool = getToolByName(self, 'portal_membership')
+        authMember = mtool.getAuthenticatedMember()
+        skiped = []
+        for id, new_id in zip(ids, new_ids) :
+            if id == new_id : continue
+            
+            ob = self._getOb(id)
+            if authMember.allowed(ob, object_roles=['Owner'] ) and \
+               _checkPermission(ModifyPortalContent, ob) :
+                self.manage_renameObject(id, new_id)
+            else :
+                skiped.append(id)
+        
+        if REQUEST is not None :
+            return self.manage_main(self, REQUEST, update_menu=1)
+
+        return skiped
+
+
+    security.declareProtected(ListFolderContents, 'listFolderContents')
+    def listFolderContents( self, contentFilter=None ):
+        """ List viewable contentish and folderish sub-objects.
+        """
+        items = self.contentItems(filter=contentFilter)
+        l = []
+        for id, obj in items:
+            if _checkPermission(View, obj) :
+                l.append(obj)
+
+        return l
+
+
+    security.declareProtected(ListFolderContents, 'listNearestFolderContents')
+    def listNearestFolderContents(self, contentFilter=None, userid=None, sorted=False) :
+        """ Return folder contents and traverse
+        recursively unaccessfull sub folders to find
+        accessible contents.
+        """
+
+        filt = {}
+        if contentFilter :
+            filt = contentFilter.copy()
+        ctool = getToolByName(self, 'portal_catalog')
+        mtool = getToolByName(self, 'portal_membership')
+
+        if userid and _checkPermission(CheckMemberPermission, getToolByName(self, 'portal_url').getPortalObject()) :
+            checkFunc = lambda perm, ob : _checkMemberPermission(userid, View, ob)
+            filt['allowedRolesAndUsers'] = ctool._listAllowedRolesAndUsers( mtool.getMemberById(userid) )
+        else :
+            checkFunc = _checkPermission
+            filt['allowedRolesAndUsers'] = ctool._listAllowedRolesAndUsers( mtool.getAuthenticatedMember() )
+        
+        
+        # copy from CMFCore.PortalFolder.PortalFolder._filteredItems
+        pt = filt.get('portal_type', [])
+        if type(pt) is type(''):
+            pt = [pt]
+        types_tool = getToolByName(self, 'portal_types')
+        allowed_types = types_tool.listContentTypes()
+        if not pt:
+            pt = allowed_types
+        else:
+            pt = [t for t in pt if t in allowed_types]
+        if not pt:
+            # After filtering, no types remain, so nothing should be
+            # returned.
+            return []
+        filt['portal_type'] = pt
+        #---
+
+        query = ContentFilter(**filt)
+        nearestObjects = []
+        
+        for o in self.objectValues() :
+            if query(o) :
+                if checkFunc(View, o):
+                    nearestObjects.append(o)
+                elif getattr(o.aq_self,'isAnObjectManager', False):
+                    nearestObjects.extend(_getDeepObjects(self, ctool, o, filter=filt))
+                
+        if sorted and len(nearestObjects) > 0 :
+            key, reverse = self.getDefaultSorting()
+            if key != 'position' :
+                indexCallable = callable(getattr(nearestObjects[0], key))
+                if indexCallable :
+                    sortfunc = lambda a, b : cmp(getattr(a, key)(), getattr(b, key)())
+                else :                       
+                    sortfunc = lambda a, b : cmp(getattr(a, key), getattr(b, key))
+                nearestObjects.sort(cmp=sortfunc, reverse=reverse)
+        
+        return nearestObjects
+    
+    security.declareProtected(ListFolderContents, 'listCatalogedContents')
+    def listCatalogedContents(self, contentFilter={}):
+        """ query catalog and returns brains of contents.
+            Requires ExtendedPathIndex
+        """
+        ctool = getUtilityByInterfaceName('Products.CMFCore.interfaces.ICatalogTool')
+        contentFilter['path'] = {'query':'/'.join(self.getPhysicalPath()),
+                                'depth':1}
+        if not contentFilter.has_key('sort_on') :
+            contentFilter['sort_index'] = 'position'
+        return ctool(**contentFilter)    
+
+    security.declarePublic('synContentValues')
+    def synContentValues(self):
+        # value for syndication
+        return self.listNearestFolderContents()
+
+    security.declareProtected(View, 'SearchableText')
+    def SearchableText(self) :
+        """ for full text indexation
+        """
+        return '%s %s' % (self.title, self.description)
+
+    security.declareProtected(AddPortalFolders, 'manage_addPlinnFolder')
+    def manage_addPlinnFolder(self, id, title='', REQUEST=None):
+        """Add a new PortalFolder object with id *id*.
+        """
+        ob=PlinnFolder(id, title)
+        # from CMFCore.PortalFolder.PortalFolder :-)
+        self._setObject(id, ob)
+        if REQUEST is not None:
+            return self.folder_contents( # XXX: ick!
+                self, REQUEST, portal_status_message="Folder added")
+    
+    
+    security.declareProtected(AddPortalContent, 'put_upload')
+    def put_upload(self, REQUEST, RESPONSE):
+        """ Upload a content thru webdav put method.
+            The default behavior (NullRessource.PUT + PortalFolder.PUT_factory)
+            disallow files names with '_' at the begining.
+        """
+
+        self.dav__init(REQUEST, RESPONSE)
+        fileName = unquote(REQUEST.getHeader('X-File-Name', ''))
+        validId = makeValidId(self, fileName, allow_dup=True)
+
+        ifhdr = REQUEST.get_header('If', '')
+        if self.wl_isLocked():
+            if ifhdr:
+                self.dav__simpleifhandler(REQUEST, RESPONSE, col=1)
+            else:
+                raise Locked
+        elif ifhdr:
+            raise PreconditionFailed
+
+        if int(REQUEST.get('CONTENT_LENGTH') or 0) > LARGE_FILE_THRESHOLD:
+            file = REQUEST['BODYFILE']
+            body = file.read(LARGE_FILE_THRESHOLD)
+            file.seek(0)
+        else:
+            body = REQUEST.get('BODY', '')
+
+        typ=REQUEST.get_header('content-type', None)
+        if typ is None:
+            typ, enc=guess_content_type(validId, body)
+
+        if self.checkIdAvailable(validId) :
+            try :
+                ob = self.PUT_factory(validId, typ, body)
+                self._setObject(validId, ob)
+                ob = self._getOb(validId)
+            except ValueError : # maybe "Disallowed subobject type". Fallback to file type.
+                validId = self.invokeFactory('File', validId)
+                ob = self._getOb(validId)
+            if IDublinCore.providedBy(ob) :
+                ob.editMetadata(title=fileName,
+                                format=typ)
+            httpRespCode = 201
+        else :
+            httpRespCode = 200
+            ob = self._getOb(validId)
+
+        # We call _verifyObjectPaste with verify_src=0, to see if the
+        # user can create this type of object (and we don't need to
+        # check the clipboard.
+        try:
+            self._verifyObjectPaste(ob.__of__(self), 0)
+        except CopyError:
+             sMsg = 'Unable to create object of class %s in %s: %s' % \
+                    (ob.__class__, repr(self), sys.exc_info()[1],)
+             raise Unauthorized, sMsg
+
+        ob.PUT(REQUEST, RESPONSE)
+        ob.orig_name = fileName
+        
+        # get method from ob created / refreshed
+        ti = ob.getTypeInfo()
+        method_id = ti.queryMethodID('jsupload_snippet')
+        meth = getattr(ob, method_id) if method_id else None
+        if not meth :
+            # get method from container that receive uploaded content
+            ti = self.getTypeInfo()
+            method_id = ti.queryMethodID('jsupload_snippet')
+            meth = getattr(self, method_id) if method_id else lambda ob : 'Not implemented'
+
+        RESPONSE.setStatus(httpRespCode)
+        RESPONSE.setHeader('Content-Type', 'text/xml;;charset=utf-8')
+        return '<fragment>%s</fragment>' % meth(ob).strip()
+
+    
+#   ## overload to maintain ownership if authenticated user has 'Manage portal' permission
+#   def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
+#       """Paste previously copied objects into the current object.
+#
+#       If calling manage_pasteObjects from python code, pass the result of a
+#       previous call to manage_cutObjects or manage_copyObjects as the first
+#       argument.
+#
+#       Also sends IObjectCopiedEvent and IObjectClonedEvent
+#       or IObjectWillBeMovedEvent and IObjectMovedEvent.
+#       """
+#       if cb_copy_data is not None:
+#           cp = cb_copy_data
+#       elif REQUEST is not None and REQUEST.has_key('__cp'):
+#           cp = REQUEST['__cp']
+#       else:
+#           cp = None
+#       if cp is None:
+#           raise CopyError, eNoData
+#
+#       try:
+#           op, mdatas = _cb_decode(cp)
+#       except:
+#           raise CopyError, eInvalid
+#
+#       oblist = []
+#       app = self.getPhysicalRoot()
+#       for mdata in mdatas:
+#           m = Moniker.loadMoniker(mdata)
+#           try:
+#               ob = m.bind(app)
+#           except ConflictError:
+#               raise
+#           except:
+#               raise CopyError, eNotFound
+#           self._verifyObjectPaste(ob, validate_src=op+1)
+#           oblist.append(ob)
+#
+#       result = []
+#       if op == 0:
+#           # Copy operation
+#           mtool = getToolByName(self, 'portal_membership')
+#           utool = getToolByName(self, 'portal_url')
+#           portal = utool.getPortalObject()
+#           userIsPortalManager = mtool.checkPermission(ManagePortal, portal)
+#
+#           for ob in oblist:
+#               orig_id = ob.getId()
+#               if not ob.cb_isCopyable():
+#                   raise CopyError, eNotSupported % escape(orig_id)
+#
+#               try:
+#                   ob._notifyOfCopyTo(self, op=0)
+#               except ConflictError:
+#                   raise
+#               except:
+#                   raise CopyError, MessageDialog(
+#                       title="Copy Error",
+#                       message=sys.exc_info()[1],
+#                       action='manage_main')
+#
+#               id = self._get_id(orig_id)
+#               result.append({'id': orig_id, 'new_id': id})
+#
+#               orig_ob = ob
+#               ob = ob._getCopy(self)
+#               ob._setId(id)
+#               notify(ObjectCopiedEvent(ob, orig_ob))
+#               
+#               if not userIsPortalManager :
+#                   self._setObject(id, ob, suppress_events=True)
+#               else :
+#                   self._setObject(id, ob, suppress_events=True, set_owner=0)
+#               ob = self._getOb(id)
+#               ob.wl_clearLocks()
+#
+#               ob._postCopy(self, op=0)
+#
+#               OFS.subscribers.compatibilityCall('manage_afterClone', ob, ob)
+#
+#               notify(ObjectClonedEvent(ob))
+#
+#           if REQUEST is not None:
+#               return self.manage_main(self, REQUEST, update_menu=1,
+#                                       cb_dataValid=1)
+#
+#       elif op == 1:
+#           # Move operation
+#           for ob in oblist:
+#               orig_id = ob.getId()
+#               if not ob.cb_isMoveable():
+#                   raise CopyError, eNotSupported % escape(orig_id)
+#
+#               try:
+#                   ob._notifyOfCopyTo(self, op=1)
+#               except ConflictError:
+#                   raise
+#               except:
+#                   raise CopyError, MessageDialog(
+#                       title="Move Error",
+#                       message=sys.exc_info()[1],
+#                       action='manage_main')
+#
+#               if not sanity_check(self, ob):
+#                   raise CopyError, "This object cannot be pasted into itself"
+#
+#               orig_container = aq_parent(aq_inner(ob))
+#               if aq_base(orig_container) is aq_base(self):
+#                   id = orig_id
+#               else:
+#                   id = self._get_id(orig_id)
+#               result.append({'id': orig_id, 'new_id': id})
+#
+#               notify(ObjectWillBeMovedEvent(ob, orig_container, orig_id,
+#                                             self, id))
+#
+#               # try to make ownership explicit so that it gets carried
+#               # along to the new location if needed.
+#               ob.manage_changeOwnershipType(explicit=1)
+#
+#               try:
+#                   orig_container._delObject(orig_id, suppress_events=True)
+#               except TypeError:
+#                   orig_container._delObject(orig_id)
+#                   warnings.warn(
+#                       "%s._delObject without suppress_events is discouraged."
+#                       % orig_container.__class__.__name__,
+#                       DeprecationWarning)
+#               ob = aq_base(ob)
+#               ob._setId(id)
+#
+#               try:
+#                   self._setObject(id, ob, set_owner=0, suppress_events=True)
+#               except TypeError:
+#                   self._setObject(id, ob, set_owner=0)
+#                   warnings.warn(
+#                       "%s._setObject without suppress_events is discouraged."
+#                       % self.__class__.__name__, DeprecationWarning)
+#               ob = self._getOb(id)
+#
+#               notify(ObjectMovedEvent(ob, orig_container, orig_id, self, id))
+#               notifyContainerModified(orig_container)
+#               if aq_base(orig_container) is not aq_base(self):
+#                   notifyContainerModified(self)
+#
+#               ob._postCopy(self, op=1)
+#               # try to make ownership implicit if possible
+#               ob.manage_changeOwnershipType(explicit=0)
+#
+#           if REQUEST is not None:
+#               REQUEST['RESPONSE'].setCookie('__cp', 'deleted',
+#                                   path='%s' % cookie_path(REQUEST),
+#                                   expires='Wed, 31-Dec-97 23:59:59 GMT')
+#               REQUEST['__cp'] = None
+#               return self.manage_main(self, REQUEST, update_menu=1,
+#                                       cb_dataValid=0)
+#
+#       return result
+
+        
 InitializeClass(PlinnFolder)
 PlinnFolderFactory = Factory(PlinnFolder)
 
 def _getDeepObjects(self, ctool, o, filter={}):
-       res = ctool.unrestrictedSearchResults(path = '/'.join(o.getPhysicalPath()), **filter)
-       
-       if not res :
-               return []
-       else :
-               deepObjects = []
-               res = list(res)
-               res.sort(lambda a, b: cmp(a.getPath(), b.getPath()))
-               previousPath = res[0].getPath()
-
-               deepObjects.append(res[0].getObject())
-               for b in res[1:] :
-                       currentPath = b.getPath()
-                       if currentPath.startswith(previousPath) and len(currentPath) > len(previousPath):
-                               continue
-                       else :
-                               deepObjects.append(b.getObject())
-                               previousPath = currentPath
-
-               return deepObjects
+    res = ctool.unrestrictedSearchResults(path = '/'.join(o.getPhysicalPath()), **filter)
+    
+    if not res :
+        return []
+    else :
+        deepObjects = []
+        res = list(res)
+        res.sort(lambda a, b: cmp(a.getPath(), b.getPath()))
+        previousPath = res[0].getPath()
+
+        deepObjects.append(res[0].getObject())
+        for b in res[1:] :
+            currentPath = b.getPath()
+            if currentPath.startswith(previousPath) and len(currentPath) > len(previousPath):
+                continue
+            else :
+                deepObjects.append(b.getObject())
+                previousPath = currentPath
+
+        return deepObjects
 
 
 manage_addPlinnFolder = PlinnFolder.manage_addPlinnFolder.im_func