systemObjects.py :  » Web-Server » Porcupine-Web-Application-Server » porcupine-0.6-src » porcupine » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Server » Porcupine Web Application Server 
Porcupine Web Application Server » porcupine 0.6 src » porcupine » systemObjects.py
#===============================================================================
#    Copyright 2005-2009, Tassos Koutsovassilis
#
#    This file is part of Porcupine.
#    Porcupine is free software; you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as published by
#    the Free Software Foundation; either version 2.1 of the License, or
#    (at your option) any later version.
#    Porcupine 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 Lesser General Public License for more details.
#    You should have received a copy of the GNU Lesser General Public License
#    along with Porcupine; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#===============================================================================
"""
System top-level Porcupine content classes.
Use these as base classes to create you own custom objects.

@see: L{org.innoscript.desktop.schema} module as a usage guideline.
"""
import time
import copy

from porcupine import context
from porcupine import db
from porcupine import exceptions
from porcupine import datatypes
from porcupine.core.objectSet import ObjectSet
from porcupine.utils import misc,permsresolver
from porcupine.core.decorators import deprecated

class _Shortcuts(datatypes.RelatorN):
    "Data type for keeping the shortcuts IDs that an object has"
    relCc = ('porcupine.systemObjects.Shortcut',)
    relAttr = 'target'
    cascadeDelete = True
    
class _TargetItem(datatypes.Relator1):
    "The object ID of the target item of the shortcut."
    relCc = ('porcupine.systemObjects.Item',)
    relAttr = 'shortcuts'
    isRequired = True

# deprecated datatype (used for backwards compatibility)
displayName = datatypes.RequiredString

#================================================================================
# Porcupine server top level content classes
#================================================================================

class Cloneable(object):
    """
    Adds cloning capabilities to Porcupine Objects.
    
    Adding I{Cloneable} to the base classes of a class
    makes instances of this class cloneable, allowing item copying.
    """
    def _copy(self, target, clear_inherited=False):
        clone = self.clone()
        if clear_inherited:
            clone.inheritRoles = False
        
        user = context.user
        clone._owner = user._id
        clone._created = time.time()
        clone.modifiedBy = user.displayName.value
        clone.modified = time.time()
        clone._parentid = target._id
        
        db._db.handle_update(clone, None)
        db._db.put_item(clone)

        if self.isCollection:
            [child._copy(clone) for child in self.get_children()]

    def clone(self, dup_ext_files=True):
        """
        Creates an in-memory clone of the item.
        This is a shallow copy operation meaning that the item's
        references are not cloned.
        
        @param dup_ext_files: Boolean indicating if the external
                              files should be also duplicated
        @type dup_ext_files: bool
        @return: the clone object
        @rtype: type
        """
        clone = copy.deepcopy(self, {'df':dup_ext_files})
        clone._id = misc.generate_oid()
        return clone

    @db.requires_transactional_context
    def copy_to(self, target_id, trans=None):
        """
        Copies the item to the designated target.

        @param target_id: The ID of the destination container
        @type target_id: str
        @param trans: A valid transaction handle
        @return: None
        @raise L{porcupine.exceptions.ObjectNotFound}:
            If the target container does not exist.
        """
        target = db._db.get_item(target_id)
        if target == None or target._isDeleted:
            raise exceptions.ObjectNotFound, (
                'The target container "%s" does not exist.' % target_id)
        
        if isinstance(self, Shortcut):
            contentclass = self.get_target_contentclass()
        else:
            contentclass = self.get_contentclass()
        
        if self.isCollection and target.is_contained_in(self._id):
            raise exceptions.ContainmentError, \
                'Cannot copy item to destination.\n' + \
                'The destination is contained in the source.'
        
        # check permissions on target folder
        user = context.user
        user_role = permsresolver.get_access(target, user)
        if not(self._isSystem) and user_role > permsresolver.READER:
            if not(contentclass in target.containment):
                raise exceptions.ContainmentError, \
                    'The target container does not accept ' + \
                    'objects of type\n"%s".' % contentclass
            
            self._copy(target, clear_inherited=True)
            # update parent
            target.modified = time.time()
            db._db.put_item(target)
        else:
            raise exceptions.PermissionDenied, \
                'The object was not copied.\n' + \
                'The user has insufficient permissions.'
    copyTo = deprecated(copy_to)

class Movable(object):
    """
    Adds moving capabilities to Porcupine Objects.
    
    Adding I{Movable} to the base classes of a class
    makes instances of this class movable, allowing item moving.
    """
    @db.requires_transactional_context
    def move_to(self, target_id, trans=None):
        """
        Moves the item to the designated target.
        
        @param target_id: The ID of the destination container
        @type target_id: str
        @param trans: A valid transaction handle
        @return: None
        @raise L{porcupine.exceptions.ObjectNotFound}:
            If the target container does not exist.
        """
        user = context.user
        user_role = permsresolver.get_access(self, user)
        can_move = (user_role > permsresolver.AUTHOR)
        ## or (user_role == permsresolver.AUTHOR and oItem.owner == user.id)

        parent_id = self._parentid
        target = db._db.get_item(target_id)
        if target == None or target._isDeleted:
            raise exceptions.ObjectNotFound, (
                'The target container "%s" does not exist.' % target_id)
        
        if isinstance(self, Shortcut):
            contentclass = self.get_target_contentclass()
        else:
            contentclass = self.get_contentclass()
        
        user_role2 = permsresolver.get_access(target, user)
        
        if self.isCollection and target.is_contained_in(self._id):
            raise exceptions.ContainmentError, \
                'Cannot move item to destination.\n' + \
                'The destination is contained in the source.'
        
        if (not(self._isSystem) and can_move and
                user_role2 > permsresolver.READER):
            if not(contentclass in target.containment):
                raise exceptions.ContainmentError, \
                    'The target container does not accept ' + \
                    'objects of type\n"%s".' % contentclass
            
            self._parentid = target._id
            self.inheritRoles = False
            self.modified = time.time()
            db._db.check_unique(self, None)
            db._db.put_item(self)

            # update target
            target.modified = time.time()
            db._db.put_item(target)

            # update parent
            parent = db._db.get_item(parent_id)
            parent.modified = time.time()
            db._db.put_item(parent)
        else:
            raise exceptions.PermissionDenied, \
                'The object was not moved.\n' + \
                'The user has insufficient permissions.'
    moveTo = deprecated(move_to)

class Removable(object):
    """
    Makes Porcupine objects removable.
    
    Adding I{Removable} to the base classes of a class
    makes instances of this type removable.
    Instances of this type can be either logically
    deleted - (moved to a L{RecycleBin} instance) - or physically
    deleted.
    """
    def _delete(self):
        """
        Deletes the item physically.
        
        @param trans: A valid transaction handle
        @return: None
        """
        db._db.handle_delete(self, True)
        db._db.delete_item(self)
        
        if self.isCollection:
            cursor = db._db.query_index('_parentid', self._id)
            cursor.fetch_all = True
            [child._delete() for child in cursor]
            cursor.close()

    @db.requires_transactional_context
    def delete(self, trans=None):
        """
        Deletes the item permanently.
        
        @param trans: A valid transaction handle
        @return: None
        """
        user = context.user
        self = db._db.get_item(self._id)

        user_role = permsresolver.get_access(self, user)
        can_delete = (user_role > permsresolver.AUTHOR) or \
            (user_role == permsresolver.AUTHOR and self._owner == user._id)
        
        if (not(self._isSystem) and can_delete):
            # delete item physically
            self._delete()
            # update container
            parent = db._db.get_item(self._parentid)
            parent.modified = time.time()
            db._db.put_item(parent)
        else:
            raise exceptions.PermissionDenied, \
                'The object was not deleted.\n' + \
                'The user has insufficient permissions.'
    
    def _recycle(self):
        """
        Deletes an item logically.
        Bypasses security checks.
        
        @return: None
        """
        if not self._isDeleted:
            db._db.handle_delete(self, False)
        
        self._isDeleted = int(self._isDeleted) + 1
        
        if self.isCollection:
            cursor = db._db.query_index('_parentid', self._id)
            cursor.fetch_all = True
            [child._recycle() for child in cursor]
            cursor.close()
        
        db._db.put_item(self)
        
    def _undelete(self):
        """
        Undeletes a logically deleted item.
        Bypasses security checks.
        
        @return: None
        """
        if int(self._isDeleted) == 1:
            db._db.handle_undelete(self)
        
        self._isDeleted = int(self._isDeleted) - 1
        
        if self.isCollection:
            cursor = db._db.query_index('_parentid', self._id)
            cursor.fetch_all = True
            [child._undelete() for child in cursor]
            cursor.close()
        
        db._db.put_item(self)

    @db.requires_transactional_context
    def recycle(self, rb_id, trans=None):
        """
        Moves the item to the specified recycle bin.
        The item then becomes inaccessible.
        
        @param rb_id: The id of the destination container, which must be
                      a L{RecycleBin} instance
        @type rb_id: str
        @param trans: A valid transaction handle
        @return: None
        """
        user = context.user
        self = db._db.get_item(self._id)
        
        user_role = permsresolver.get_access(self, user)
        can_delete = (user_role > permsresolver.AUTHOR) or \
                     (user_role == permsresolver.AUTHOR and
                      self._owner == user._id)
        
        if (not(self._isSystem) and can_delete):
            deleted = DeletedItem(self)
            deleted._owner = user._id
            deleted._created = time.time()
            deleted.modifiedBy = user.displayName.value
            deleted.modified = time.time()
            deleted._parentid = rb_id
            
            # check recycle bin's containment
            recycle_bin = db._db.get_item(rb_id)
            if not(deleted.get_contentclass() in recycle_bin.containment):
                raise exceptions.ContainmentError, \
                    'The target container does not accept ' + \
                    'objects of type\n"%s".' % deleted.get_contentclass()
            
            db._db.handle_update(deleted, None)
            db._db.put_item(deleted)
            
            # delete item logically
            self._recycle()
            
            # update container
            parent = db._db.get_item(self._parentid)
            parent.modified = time.time()
            db._db.put_item(parent)
        else:
            raise exceptions.PermissionDenied, \
                'The object was not deleted.\n' + \
                'The user has insufficient permissions.'

class Composite(object):
    """Objects within Objects...
    
    Think of this as an embedded item. This class is useful
    for implementing compositions. Instances of this class
    are embedded into other items.
    Note that instances of this class have no
    security descriptor since they are embedded into other items.
    The L{security} property of such instances is actually a proxy to
    the security attribute of the object that embeds this object.
    Moreover, they do not have parent containers the way
    instances of L{GenericItem} have.
    
    @type contentclass: str
    @type id: str
    @type security: dict
    @see: L{porcupine.datatypes.Composition}.
    """
    __image__ = "desktop/images/object.gif"
    __props__ = ()
    _eventHandlers = []

    def __init__(self):
        self._id = misc.generate_oid()
        self._containerid = None
        self._isDeleted = 0
        
        self.displayName = datatypes.RequiredString()

    def get_security(self):
        """Getter of L{security} property
        
        @rtype: dict
        """
        return db._db.get_item(self._containerid).security
    security = property(get_security)

    def get_id(self):
        """Getter of L{id} property
        
        @rtype: str
        """
        return self._id
    id = property(get_id)

    def get_contentclass(self):
        """Getter of L{contentclass} property
        
        @rtype: str
        """
        return '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
    contentclass = property(get_contentclass)

class GenericItem(object):
    """Generic Item
    The base class of all Porcupine objects.
    
    @cvar __props__: A tuple containing all the object's custom data types.
    @type __props__: tuple
    @cvar _eventHandlers: A list containing all the object's event handlers.
    @type _eventHandlers: list
    @cvar isCollection: A boolean indicating if the object is a container.
    @type isCollection: bool
    @ivar modifiedBy: The display name of the last modifier.
    @type modifiedBy: str
    @ivar modified: The last modification date, handled by the server.
    @type modified: float
    @ivar security: The object's security descriptor. This is a dictionary whose
                    keys are the users' IDs and the values are the roles.
    @type security: dict
    @ivar inheritRoles: Indicates if the object's security
                        descriptor is identical to this of its parent
    @type inheritRoles: bool
    @ivar displayName: The display name of the object.
    @type displayName: L{RequiredString<porcupine.datatypes.RequiredString>}
    @ivar description: A short description.
    @type description: L{String<porcupine.datatypes.String>}
    @type contentclass: str
    @type created: float
    @type id: str
    @type issystem: bool
    @type owner: type
    @type parentid: str
    """
    __image__ = "desktop/images/object.gif"
    __props__ = ('displayName', 'description')
    isCollection = False
    _eventHandlers = []

    def __init__(self):
        # system props
        self._id = misc.generate_oid()
        self._parentid = None
        self._owner = ''
        self._isSystem = False
        self._isDeleted = 0
        self._created = 0
        
        self.modifiedBy = ''
        self.modified = 0
        self.security = {}
        self.inheritRoles = True

        self.displayName = datatypes.RequiredString()
        self.description = datatypes.String()

    def _apply_security(self, parent, is_new):
        if self.inheritRoles:
            self.security = parent.security
        if self.isCollection and not is_new:
            cursor = db._db.query_index('_parentid', self._id)
            cursor.fetch_all = True
            for child in cursor:
                child._apply_security(self, is_new)
                db._db.put_item(child)
            cursor.close()

    @db.requires_transactional_context
    def append_to(self, parent, trans=None):
        """
        Adds the item to the specified container.

        @param parent: The id of the destination container or the container
                       itself
        @type parent: str OR L{Container}
        @param trans: A valid transaction handle
        @return: None
        """
        if type(parent) == str:
            parent = db._db.get_item(parent)
        
        if isinstance(self, Shortcut):
            contentclass = self.get_target_contentclass()
        else:
            contentclass = self.get_contentclass()
        
        user = context.user
        user_role = permsresolver.get_access(parent, user)
        if user_role == permsresolver.READER:
            raise exceptions.PermissionDenied, \
                'The user does not have write permissions ' + \
                'on the parent folder.'
        if not(contentclass in parent.containment):
            raise exceptions.ContainmentError, \
                'The target container does not accept ' + \
                'objects of type\n"%s".' % contentclass

        # set security to new item
        if user_role == permsresolver.COORDINATOR:
            # user is COORDINATOR
            self._apply_security(parent, True)
        else:
            # user is not COORDINATOR
            self.inheritRoles = True
            self.security = parent.security
        
        self._owner = user._id
        self._created = time.time()
        self.modifiedBy = user.displayName.value
        self.modified = time.time()
        self._parentid = parent._id
        db._db.handle_update(self, None)
        parent.modified = self.modified
        db._db.put_item(self)
        db._db.put_item(parent)
    appendTo = deprecated(append_to)
    
    def is_contained_in(self, item_id, trans=None):
        """
        Checks if the item is contained in the specified container.
        
        @param item_id: The id of the container
        @type item_id: str
        @rtype: bool
        """
        item = self
        while item._id != '':
            if item._id == item_id:
                return True
            item = db._db.get_item(item.parentid)
        return False
    isContainedIn = deprecated(is_contained_in)
    
    def get_parent(self, trans=None):
        """
        Returns the parent container.
                
        @param trans: A valid transaction handle
        @return: the parent container object
        @rtype: type
        """
        return db.get_item(self._parentid)
    getParent = deprecated(get_parent)
    
    def get_all_parents(self, trans=None):
        """
        Returns all the parents of the item traversing the
        hierarchy up to the root folder.
        
        @rtype: L{ObjectSet<porcupine.core.objectSet.ObjectSet>}
        """
        parents = []
        item = self
        while item and item._id:
            parents.append(item)
            item = item.get_parent()
        parents.reverse()
        return ObjectSet(parents)
    getAllParents = deprecated(get_all_parents)
    
    def get_contentclass(self):
        """Getter of L{contentclass} property
        
        @rtype: str
        """
        return '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
    contentclass = property(get_contentclass, None, None,
                            "The type of the object")
    
    def get_id(self):
        """Getter of L{id} property
        
        @rtype: str
        """
        return self._id
    id = property(get_id, None, None, "The ID of the object")
    
    def get_is_system(self):
        """Getter of L{issystem} property
        
        @rtype: bool
        """
        return self._isSystem
    issystem = property(get_is_system, None, None,
                        "Indicates if this is a systemic object")
    isSystem = property(deprecated(get_is_system, "issystem"), None, None,
                        "Deprecated property. Use issystem instead.")
    
    def get_owner(self):
        """Getter of L{owner} property
        
        @rtype: type
        """
        return self._owner
    owner = property(get_owner, None, None, "The object's creator")
    
    def get_created(self):
        """Getter of L{created} property
        
        @rtype: float
        """
        return self._created
    created = property(get_created, None, None, "The creation date")
    
    def get_parent_id(self):
        """Getter of L{parentid} property
        
        @rtype: str
        """
        return self._parentid
    parentid = property(get_parent_id, None, None,
                        "The ID of the parent container")

#================================================================================
# Porcupine server system classes
#================================================================================

class DeletedItem(GenericItem, Removable):
    """
    This is the type of items appended into a L{RecycleBin} class instance.
    
    L{RecycleBin} containers accept objects of this type only.
    Normally, you won't ever need to instantiate an item of this
    type. Instantiations of this class are handled by the server
    internally when the L{Removable.recycle} method is called.
    
    @ivar originalName: The display name of the deleted object.
    @type originalName: str
    @ivar originalLocation: The path to the location of the deleted item
                            before the deletion
    @type originalLocation: str
    """
    def __init__(self, deleted_item, trans=None):
        GenericItem.__init__(self)

        self.inheritRoles = True
        self._deletedId = deleted_item._id
        self.__image__ = deleted_item.__image__
        
        self.displayName.value = misc.generate_oid()
        self.description.value = deleted_item.description.value
        
        parents = deleted_item.get_all_parents()
        full_path = '/'
        full_path += '/'.join([p.displayName.value for p in parents[:-1]])
        self.originalLocation = full_path
        self.originalName = deleted_item.displayName.value

    def _restore(self, deleted, target):
        """
        Restores a logically deleted item to the designated target.
        
        @return: None
        """
        # check permissions
        user = context.user
        user_role = permsresolver.get_access(target, user)
        
        if user_role > permsresolver.READER:
            deleted._parentid = target._id
            deleted.inheritRoles = False
            deleted._undelete()
        else:
            raise exceptions.PermissionDenied, \
                    'The user does not have write permissions on the ' + \
                    'destination folder.'

    def get_deleted_item(self):
        """
        Use this method to get the item that was logically deleted.
            
        @return: the deleted item
        @rtype: L{GenericItem}
        """
        return db._db.get_item(self._deletedId)
    getDeletedItem = deprecated(get_deleted_item)

    def append_to(self, *args, **kwargs):
        """
        Calling this method raises an ContainmentError.
        This is happening because you can not add a DeletedItem
        directly to the store.
        This type of item is added in the database only if
        the L{Removable.recycle} method is called.

        @warning: DO NOT USE.
        @raise L{porcupine.exceptions.ContainmentError}: Always
        """
        raise exceptions.ContainmentError, \
            'Cannot directly add this item to the store.\n' + \
            'Use the "recycle" method instead.'
    appendTo = deprecated(append_to)

    @db.requires_transactional_context
    def restore(self, trans=None):
        """
        Restores the deleted item to its original location, if
        it still exists.
        
        @param trans: A valid transaction handle
        @return: None
        @raise L{porcupine.exceptions.ObjectNotFound}:
            If the original location or the original item no longer exists.
        """
        self.restore_to(None)

    @db.requires_transactional_context
    def restore_to(self, parent_id, trans=None):
        """
        Restores the deleted object to the specified container.
        
        @param parent_id: The ID of the container in which
                          the item will be restored
        @type parent_id: str    
        @param trans: A valid transaction handle
        @return: None
        @raise L{porcupine.exceptions.ObjectNotFound}:
            If the original location or the original item no longer exists.
        """
        deleted = db._db.get_item(self._deletedId)
        if deleted == None:
            raise exceptions.ObjectNotFound, (
                'Cannot locate original item.\n' +
                'It seems that this item resided in a container\n' +
                'that has been permanently deleted or it is shortcut\n' +
                'having its target permanently deleted.')
        parent = db._db.get_item(parent_id or deleted._parentid)
        if parent == None or parent._isDeleted:
            raise exceptions.ObjectNotFound, (
                'Cannot locate target container.\n' +
                'It seems that this container is deleted.')
        
        if isinstance(deleted, Shortcut):
            contentclass = deleted.get_target_contentclass()
        else:
            contentclass = deleted.get_contentclass()
        
        if contentclass and not(contentclass in parent.containment):
            raise exceptions.ContainmentError, \
                'The target container does not accept ' + \
                'objects of type\n"%s".' % contentclass
        
        # try to restore original item
        self._restore(deleted, parent)
        # update parent
        parent.modified = time.time()
        db._db.put_item(parent)
        # delete self
        self.delete(_remove_deleted=False)
    restoreTo = deprecated(restore_to)

    @db.requires_transactional_context
    def delete(self, trans=None, _remove_deleted=True):
        """
        Deletes the deleted object permanently.
        
        @param trans: A valid transaction handle
        @param _remove_deleted: Leave as is
        @return: None
        """
        Removable.delete(self)
        if _remove_deleted:
            # we got a direct call. remove deleted item
            deleted = db._db.get_item(self._deletedId)
            if deleted != None:
                deleted._delete()

class Item(GenericItem, Cloneable, Movable, Removable):
    """
    Simple item with no versioning capabilities.
    
    Normally this is the base class of your custom Porcupine Objects
    if versioning is not required.
    Subclass the L{porcupine.systemObjects.Container} class if you want
    to create custom containers.
    """
    __props__ = GenericItem.__props__ + ('shortcuts',)
    
    def __init__(self):
        GenericItem.__init__(self)
        self.shortcuts = _Shortcuts()

    @db.requires_transactional_context
    def update(self, trans=None):
        """
        Updates the item.
        
        @param trans: A valid transaction handle
        @return: None
        """
        old_item = db._db.get_item(self._id)
        parent = db._db.get_item(self._parentid)
        
        user = context.user
        user_role = permsresolver.get_access(old_item, user)
        
        if user_role > permsresolver.READER:
            # set security
            if user_role == permsresolver.COORDINATOR:
                # user is COORDINATOR
                if (self.inheritRoles != old_item.inheritRoles) or \
                        (not self.inheritRoles and \
                         self.security != old_item.security):
                    self._apply_security(parent, False)
            else:
                # restore previous ACL
                self.security = old_item.security
                self.inheritRoles = old_item.inheritRoles

            db._db.handle_update(self, old_item)
            self.modifiedBy = user.displayName.value
            self.modified = time.time()
            parent.modified = self.modified
            db._db.put_item(self)
            db._db.put_item(parent)
        else:
            raise exceptions.PermissionDenied, \
                    'The user does not have update permissions.'

class Shortcut(Item):
    """
    Shortcuts act as pointers to other objects.
    
    When adding a shortcut in a container the containment
    is checked against the target's content class and not
    the shortcut's.
    When deleting an object that has shortcuts all its
    shortcuts are also deleted. Likewise, when restoring
    the object all of its shortcuts are also restored to
    their original location.
    It is valid to have shortcuts pointing to shortcuts.
    In order to resolve the terminal target object use the
    L{get_target} method.
    """
    __image__ = "desktop/images/link.png"
    __props__ = Item.__props__ + ('target',)
    
    def __init__(self):
        Item.__init__(self)
        self.target = _TargetItem()
        
    @staticmethod
    def create(target, trans=None):
        """Helper method for creating shortcuts of items.
        
        @param target: The id of the item or the item object itself
        @type parent: str OR L{Item}
        @param trans: A valid transaction handle
        @return: L{Shortcut}
        """
        if type(target) == str:
            target = db._db.get_item(target)
        shortcut = Shortcut()
        shortcut.displayName.value = target.displayName.value
        shortcut.target.value = target._id
        return shortcut
    
    def get_target(self, trans=None):
        """Returns the target item.
        
        @param trans: A valid transaction handle
        @return: the target item or C{None} if the user
                 has no read permissions
        @rtype: L{Item} or NoneType
        """
        target = None
        if self.target.value:
            target = self.target.get_item()
            while target and isinstance(target, Shortcut):
                target = target.target.get_item()
        return target
    
    def get_target_contentclass(self, trans=None):
        """Returns the content class of the target item.
        
        @param trans: A valid transaction handle
        @return: the fully qualified name of the target's
                 content class
        @rtype: str
        """
        if self.target.value:
            target = db._db.get_item(self.target.value)
            while isinstance(target, Shortcut):
                target = db._db.get_item(target.target.value)
            return target.get_contentclass()

class Container(Item):
    """
    Generic container class.
    
    Base class for all containers. Containers do not support versioning.
    
    @cvar containment: a tuple of strings with all the content types of
                       Porcupine objects that this class instance can accept.
    @type containment: tuple
    @type isCollection: bool
    """
    __image__ = "desktop/images/folder.gif"
    containment = ('porcupine.systemObjects.Shortcut',)
    isCollection = True
    
    def child_exists(self, name, trans=None):
        """
        Checks if a child with the specified name is contained
        in the container.
        
        @param name: The name of the child to check for
        @type name: str
        
        @param trans: A valid transaction handle
            
        @rtype: bool
        """
        conditions = (('_parentid', self._id), ('displayName', name))
        return db._db.test_join(conditions)
    childExists = deprecated(child_exists)
    
    def get_child_id(self, name, trans=None):
        """
        Given a name this function returns the ID of the child.
        
        @param name: The name of the child
        @type name: str
        @param trans: A valid transaction handle
        @return: The ID of the child if a child with the given name exists
                 else None.
        @rtype: str
        """
        conditions = (('_parentid', self._id), ('displayName', name))
        cursor = db._db.join(conditions)
        cursor.fetch_mode = 0
        iterator = iter(cursor)
        try:
            child = iterator.next()
        except StopIteration:
            child = None
        cursor.close()
        return child
    getChildId = deprecated(get_child_id)
    
    def get_child_by_name(self, name, trans=None):
        """
        This method returns the child with the specified name.
        
        @param name: The name of the child
        @type name: str
        @param trans: A valid transaction handle
        @return: The child object if a child with the given name exists
                 else None.
        @rtype: L{GenericItem}
        """
        conditions = (('_parentid', self._id), ('displayName', name))
        cursor = db._db.join(conditions)
        iterator = iter(cursor)
        try:
            child = iterator.next()
        except StopIteration:
            child = None
        cursor.close()
        return child
    getChildByName = deprecated(get_child_by_name)
    
    def get_children(self, trans=None, resolve_shortcuts=False):
        """
        This method returns all the children of the container.
        
        @param trans: A valid transaction handle
        @rtype: L{ObjectSet<porcupine.core.objectSet.ObjectSet>}
        """
        cursor = db._db.query_index('_parentid', self._id)
        cursor.resolve_shortcuts = resolve_shortcuts
        children = ObjectSet([c for c in cursor])
        cursor.close()
        return children
    getChildren = deprecated(get_children)
    
    def get_items(self, trans=None, resolve_shortcuts=False):
        """
        This method returns the children that are not containers.
        
        @param trans: A valid transaction handle
        @rtype: L{ObjectSet<porcupine.core.objectSet.ObjectSet>}
        """
        conditions = (('_parentid', self._id), ('isCollection', False))
        cursor = db._db.join(conditions)
        cursor.resolve_shortcuts = resolve_shortcuts
        items = ObjectSet([i for i in cursor])
        cursor.close()
        return items
    getItems = deprecated(get_items)
    
    def get_subfolders(self, trans=None, resolve_shortcuts=False):
        """
        This method returns the children that are containers.
        
        @param trans: A valid transaction handle
        @rtype: L{ObjectSet<porcupine.core.objectSet.ObjectSet>}
        """
        conditions = (('_parentid', self._id), ('isCollection', True))
        cursor = db._db.join(conditions)
        cursor.resolve_shortcuts = resolve_shortcuts
        subfolders = ObjectSet([f for f in cursor])
        cursor.close()
        return subfolders
    getSubFolders = deprecated(get_subfolders)
    
    def has_children(self, trans=None):
        """
        Checks if the container has at least one non-container child.
        
        @param trans: A valid transaction handle
        @rtype: bool
        """
        conditions = (('_parentid', self._id), ('isCollection', False))
        return db._db.test_join(conditions)
    hasChildren = deprecated(has_children)
    
    def has_subfolders(self, trans=None):
        """
        Checks if the container has at least one child container.
        
        @param trans: A valid transaction handle
        @rtype: bool
        """
        conditions = (('_parentid', self._id), ('isCollection', True))
        return db._db.test_join(conditions)
    hasSubfolders = deprecated(has_subfolders)

class RecycleBin(Container):
    """
    Recycle bin class.
    
    By default every I{RecycleBin} class instance is a system item.
    It cannot be deleted, copied, moved or recycled.
    """
    __image__ = "desktop/images/trashcan_empty8.gif"
    containment = ('porcupine.systemObjects.DeletedItem', )

    def __init__(self):
        Container.__init__(self)
        self._isSystem = True

    @db.requires_transactional_context
    def empty(self, trans=None):
        """
        This method empties the recycle bin.
        
        What this method actually does is to call the
        L{DeletedItem.delete} method for every
        L{DeletedItem} instance contained in the bin.
        
        @param trans: A valid transaction handle
        @return: None
        """
        items = self.get_items()
        [item.delete() for item in items]
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.