X-Git-Url: https://scm.cri.minesparis.psl.eu/git/Plinn.git/blobdiff_plain/3c4367d8e03450e9a73e61f4247145d2b6c86a33..959d888c17d1403d2eeecc19bc4b5e2c8d1debf6:/Folder.py?ds=inline diff --git a/Folder.py b/Folder.py deleted file mode 100644 index 8ed3cc3..0000000 --- a/Folder.py +++ /dev/null @@ -1,557 +0,0 @@ -# -*- coding: utf-8 -*- -####################################################################################### -# Plinn - http://plinn.org # -# Copyright (C) 2005-2014 Benoît Pin # -# # -# This program is free software; you can redistribute it and/or # -# modify it under the terms of the GNU General Public License # -# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. # -####################################################################################### -""" Plinn portal folder implementation - - - -""" - -from OFS.CopySupport import CopyError, eNoData, _cb_decode, eInvalid, eNotFound,\ - 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 -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, NoneType -from Products.CMFCore.permissions import ListFolderContents, View, ViewManagementScreens,\ - 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 -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 = 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 '%s' % 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 - - -manage_addPlinnFolder = PlinnFolder.manage_addPlinnFolder.im_func