+ """ 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()
+