def restore(self, trans):
     """
     Restores the deleted item to its original location, if
     it still exists.
     
     @param trans: A valid transaction handle
     
     @return: None
     
     @raise porcupine.serverExceptions.DBItemNotFound:
         If the original location no longer exists.
     """
     trans.actions.append( (self.restore, (trans, )) )
     try:
         ## TODO: check if oDeleted exists
         oDeleted = db.getDeletedItem(self._deletedId, trans)
         oOriginalParent = db.getItem(oDeleted._parentid, trans)
         
         # try to restore original item
         self._restore(oDeleted, oOriginalParent, trans)
 
         self.delete(trans, False)
         
         # update container
         oOriginalParent._addItemReference(oDeleted)
         db.putItem(oOriginalParent, trans)
     
     except serverExceptions.DBTransactionIncomplete:
         trans.retry()        
    def delete(self, trans):
        """
        Deletes the item permanently.
        
        @param trans: A valid transaction handle 
        
        @return: None
        """
        trans.actions.append( (self.delete, (trans, )))
        
        try:
            oUser = currentThread().session.user
            self = db.getItem(self._id, trans)

            iUserRole = objectAccess.getAccess(self, oUser)
            bCanDelete = (iUserRole > objectAccess.AUTHOR) or \
                (iUserRole == objectAccess.AUTHOR and self._owner == oUser._id)
            
            if (not(self._isSystem) and bCanDelete):
                # delete item physically
                self._delete(trans)
                # update container
                oParent = db.getItem(self._parentid, trans)
                oParent._removeItemReference(self)
                db.putItem(oParent, trans)
            else:
                raise serverExceptions.PermissionDenied
        
        except serverExceptions.DBTransactionIncomplete:
            trans.retry()
 def on_update(item, new_attr, old_attr, trans):
     from porcupine import datatypes
     
     # get previous value
     if old_attr:
         prvValue = old_attr.value
     else:
         prvValue = ''
     
     if new_attr.value != prvValue:
         if new_attr.value:
             oItemRef = db.getItem(new_attr.value, trans)
             if oItemRef.getContentclass() in new_attr.relCc:
                 oAttrRef = getattr(oItemRef, new_attr.relAttr)
                 if isinstance(oAttrRef, datatypes.RelatorN):
                     oAttrRef.value.append(item._id)
                 elif isinstance(oAttrRef, datatypes.Relator1):
                     oAttrRef.value = item._id
                 db.putItem(oItemRef, trans)
             else:
                 new_attr.value = ''
         if prvValue:
             oItemRef = db.getItem(prvValue, trans)
             oAttrRef = getattr(oItemRef, new_attr.relAttr)
             if isinstance(oAttrRef, datatypes.RelatorN):
                 try:
                     oAttrRef.value.remove(item._id)
                 except ValueError:
                     pass
             elif isinstance(oAttrRef, datatypes.Relator1):
                 oAttrRef.value = ''
             db.putItem(oItemRef, trans)
    def restoreTo(self, sParentId, trans):
        """
        Restores the deleted object to the specified container.
        
        @param sParentId: The ID of the container in which
            the item will be restored
        @type sParentId: str    
        
        @param trans: A valid transaction handle
            
        @return: None
        """
        trans.actions.append( (self.restoreTo, (sParentId, trans)) )
        try:
            ## TODO: check if oDeleted exists
            oDeleted = db.getDeletedItem(self._deletedId, trans)
            oParent = db.getItem(sParentId, trans)
            
            if not(oDeleted.getContentclass() in oParent.containment):
                raise serverExceptions.ContainmentError
            
            # try to restore original item
            self._restore(oDeleted, oParent, trans)
            
            self.delete(trans, False)
            
            # update container
            oParent._addItemReference(oDeleted)
            db.putItem(oParent, trans)

        except serverExceptions.DBTransactionIncomplete:
            trans.retry()  
 def _applySecurity(self, oParent, trans):
     if self.inheritRoles:
         self.security = oParent.security
     if self.isCollection:
         for sID in self._subfolders.values() + self._items.values():
             oItem = db.getItem(sID, trans)
             if oItem.inheritRoles:
                 oItem._applySecurity(self, trans)
                 db.putItem(oItem, trans)
    def appendTo(self, parent, trans):
        """
        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
        """
        trans.actions.append( (self.appendTo, (parent, trans)) )
        try:
            if type(parent)==str:
                oParent = db.getItem(parent, trans)
            else:
                oParent = parent
            oUser = currentThread().session.user
    
            iUserRole = objectAccess.getAccess(oParent, oUser)
            if iUserRole == objectAccess.READER:
                raise serverExceptions.PermissionDenied
            if not(self.getContentclass() in oParent.containment):
                raise serverExceptions.ContainmentError
    
            # set security to new item
            if iUserRole == objectAccess.COORDINATOR:
                # user is COORDINATOR
                self._applySecurity(oParent, trans)
            else:
                # user is not COORDINATOR
                self.inheritRoles = True
                self.security = oParent.security
#            if trans._retries == 0:
#                raise serverExceptions.DBTransactionIncomplete    
            self._owner = oUser._id
            self._created = time.time()
            self.modifiedBy = oUser.displayName.value
            self.modified = time.time()
            self._parentid = oParent._id
            db.handle_update(self, None, trans)
            db.putItem(self, trans)
            # update container
            oParent._addItemReference(self)
            db.putItem(oParent, trans)
            
        except serverExceptions.DBTransactionIncomplete:
            trans.retry()
 def on_delete(item, attr, trans, bPermanent):
     if bPermanent:
         if item._isDeleted:
             func_get = db.getDeletedItem
         else:
             func_get = db.getItem
         composites = [func_get(sID, trans) for sID in attr.value]
         for composite in composites:
             CompositionEventHandler.removeComposite(composite, trans)
     else:
         for sID in attr.value:
             composite = db.getItem(sID, trans)
             db.handle_delete(composite, trans, False)
             composite._isDeleted = True
             db.putItem(composite, trans)
    def _undelete(self, deletedItem, trans):
        """
        Undeletes a logically deleted item.
        
        Returns: None
        """
        db.handle_update(deletedItem, None, trans)
        deletedItem._isDeleted = False
        
        if deletedItem.isCollection:
            lstChildren = deletedItem._items.values() + deletedItem._subfolders.values()
            for sID in lstChildren:
                oChild = db.getDeletedItem(sID, trans)
                self._undelete(oChild, trans)

        db.putItem(deletedItem, trans)
    def _recycle(self, trans):
        """
        Removes an item's references and marks it as deleted.
        
        Returns: None
        """
        db.handle_delete(self, trans, False)
        self._isDeleted = True
        
        if self.isCollection:
            lstChildren = self._items.values() + self._subfolders.values()
            for sID in lstChildren:
                oChild = db.getItem(sID, trans)
                oChild._recycle(trans)

        db.putItem(self, trans)
 def on_delete(item, attr, trans, bPermanent):
     if not item._isDeleted:
         from porcupine import datatypes
         if attr.value:
             if attr.respectsReferences:
                 raise serverExceptions.ReferentialIntegrityError
             # remove reference
             oItemRef = db.getItem(attr.value, trans)
             oAttrRef = getattr(oItemRef, attr.relAttr)
             if isinstance(oAttrRef, datatypes.RelatorN):
                 try:
                     oAttrRef.value.remove(item._id)
                 except ValueError:
                     pass
             elif isinstance(oAttrRef, datatypes.Relator1):
                 oAttrRef.value = ''
             db.putItem(oItemRef, trans)
    def on_update(item, new_attr, old_attr, trans):
        from porcupine import datatypes
        # remove duplicates
        new_attr.value = list(sets.Set(new_attr.value))
        
        # get previous value
        if old_attr:
            prvValue = sets.Set(old_attr.value)
            noAccessList = RelatorNEventHandler.getNoAccessIds(old_attr, trans)
        else:
            prvValue = sets.Set()
            noAccessList = []

        # get current value
        currentValue = sets.Set(new_attr.value + noAccessList)

        if currentValue != prvValue:
            # calculate added references
            lstAdded = list(currentValue-prvValue)
            for sID in lstAdded:
                oItemRef = db.getItem(sID, trans)
                if oItemRef.getContentclass() in new_attr.relCc:
                    oAttrRef = getattr(oItemRef, new_attr.relAttr)
                    if isinstance(oAttrRef, datatypes.RelatorN):
                        oAttrRef.value.append(item._id)
                    elif isinstance(oAttrRef, datatypes.Relator1):
                        oAttrRef.value = item._id
                    db.putItem(oItemRef, trans)
                else:
                    new_attr.value.remove(sID)
    
            # calculate removed references
            lstRemoved = list(prvValue-currentValue)
            for sID in lstRemoved:
                oItemRef = db.getItem(sID, trans)
                oAttrRef = getattr(oItemRef, new_attr.relAttr)
                if isinstance(oAttrRef, datatypes.RelatorN):
                    try:
                        oAttrRef.value.remove(item._id)
                    except ValueError:
                        pass
                elif isinstance(oAttrRef, datatypes.Relator1):
                    oAttrRef.value = ''
                db.putItem(oItemRef, trans)
 def update(self, trans):
     """
     Updates the item.
     
     @param trans: A valid transaction handle
         
     @return: None
     """
     trans.actions.append( (self.update, (trans, )) )
     try:
         oOldItem = db.getItem(self._id, trans)
         
         oUser = currentThread().session.user
         iUserRole = objectAccess.getAccess(oOldItem, oUser)
         
         if iUserRole > objectAccess.READER:
             # set security
             if iUserRole == objectAccess.COORDINATOR:
                 # user is COORDINATOR
                 if (self.inheritRoles != oOldItem.inheritRoles) or \
                 (not self.inheritRoles and self.security != oOldItem.security):
                     oParent = db.getItem(self._parentid, trans)
                     self._applySecurity(oParent, trans)
             else:
                 # restore previous ACL
                 self.security = oOldItem.security
                 self.inheritRoles = oOldItem.inheritRoles
 
             db.handle_update(self, oOldItem, trans)
             self.modifiedBy = oUser.displayName.value
             self.modified = time.time()
             db.putItem(self, trans)
             
             if self.displayName.value != oOldItem.displayName.value:
                 oParent = db.getItem(self._parentid, trans)
                 oParent._removeItemReference(oOldItem)
                 oParent._addItemReference(self)
                 db.putItem(oParent, trans)
         else:
             raise serverExceptions.PermissionDenied
     
     except serverExceptions.DBTransactionIncomplete:
         trans.retry()
    def on_update(item, new_attr, old_attr, trans):
        # check containment
        if [obj for obj in new_attr.value
            if obj.getContentclass() != new_attr.compositeClass]:
            raise serverExceptions.ContainmentError
        
        dctObjects = {}
        for obj in new_attr.value:
            obj._containerid = item._id
            dctObjects[obj._id] = obj
            
        new_ids = sets.Set([obj._id for obj in new_attr.value])
        
        # get previous value
        if old_attr:
            old_ids = sets.Set(old_attr.value)
        else:
            old_ids = sets.Set()

        # calculate added composites
        lstAdded = list(new_ids - old_ids)
        for sID in lstAdded:
            db.handle_update(dctObjects[sID], None, trans)
            dctObjects[sID]._isDeleted = False
            db.putItem(dctObjects[sID], trans)
            
        # calculate constant composites
        lstConstant = list(new_ids & old_ids)
        for sID in lstConstant:
            db.handle_update(dctObjects[sID], db.getItem(sID, trans) , trans)
            db.putItem(dctObjects[sID], trans)

        # calculate removed composites
        lstRemoved = list(old_ids - new_ids)
        for sID in lstRemoved:
            composite4removal = db.getItem(sID, trans)
            CompositionEventHandler.removeComposite(composite4removal, trans)
            
        new_attr.value = list(new_ids)
    def on_delete(item, attr, trans, bPermanent):
        if not item._isDeleted:
            from porcupine import datatypes

            lstValue = attr.value
            if lstValue and attr.respectsReferences:
                raise serverExceptions.ReferentialIntegrityError
            
            # remove references
            for sID in lstValue:
                oItemRef = db.getItem(sID, trans)
                if oItemRef.getContentclass() in attr.relCc:
                    oAttrRef = getattr(oItemRef, attr.relAttr)
                    if isinstance(oAttrRef, datatypes.RelatorN):
                        try:
                            oAttrRef.value.remove(item._id)
                        except ValueError:
                            pass
                    elif isinstance(oAttrRef, datatypes.Relator1):
                        oAttrRef.value = ''
                    db.putItem(oItemRef, trans)
                else:
                    attr.value.remove(sID)
    def _copy(self, target, trans, clearRolesInherited=False):
        oCopy = self.clone()
        if clearRolesInherited:
            oCopy.inheritRoles = False

        oUser = currentThread().session.user
        oCopy._owner = oUser._id
        oCopy._created = time.time()
        oCopy.modifiedBy = oUser.displayName.value
        oCopy.modified = time.time()
        oCopy._parentid = target._id
        
        db.handle_update(oCopy, None, trans)
        db.putItem(oCopy, trans)

        if self.isCollection:
            lstChildrentIds = self._items.values() + self._subfolders.values()
            for sId in lstChildrentIds:
                child = db.getItem(sId, trans)
                if child and objectAccess.getAccess(child, oUser):
                    child._copy(oCopy, trans)

        target._addItemReference(oCopy)
        db.putItem(target, trans)
    def recycle(self, rbID, trans):
        """
        Moves the item to the specified recycle bin.
        
        The item then becomes inaccesible.
        
        @param rbID: The id of the destination container, which must be
                     a L{RecycleBin} instance
        @type rbID: str
        
        @param trans: A valid transaction handle 
        
        @return: None
        """
        trans.actions.append( (self.recycle, (rbID, trans)) )
        try:
            oUser = currentThread().session.user
            self = db.getItem(self._id, trans)
        
            iUserRole = objectAccess.getAccess(self, oUser)
            bCanDelete = (iUserRole > objectAccess.AUTHOR) or \
                (iUserRole == objectAccess.AUTHOR and self._owner == oUser._id)
        
            if (not(self._isSystem) and bCanDelete):
                oDeleted = DeletedItem(self)
    
                oDeleted._owner = oUser._id
                oDeleted._created = time.time()
                oDeleted.modifiedBy = oUser.displayName.value
                oDeleted.modified = time.time()
                oDeleted._parentid = rbID
                db.handle_update(oDeleted, None, trans)
                db.putItem(oDeleted, trans)
    
                # delete item logically
                self._recycle(trans)
                
                # save container
                oParent = db.getItem(self._parentid, trans)
                oParent._removeItemReference(self)
                db.putItem(oParent, trans)
                
                #update recycle bin
                oRecycleBin = db.getItem(rbID, trans)
                if not(oDeleted.getContentclass() in oRecycleBin.containment):
                    raise serverExceptions.ContainmentError
                oRecycleBin._addItemReference(oDeleted)
                db.putItem(oRecycleBin, trans)
            else:
                raise serverExceptions.PermissionDenied

        except serverExceptions.DBTransactionIncomplete:
            trans.retry()
    def moveTo(self, targetId, trans):
        """
        Moves the item to the designated target.
        
        @param targetId: The ID of the destination container
        @type targetId: str
        
        @param trans: A valid transaction handle 
            
        @return: None
        """
        trans.actions.append( (self.moveTo, (targetId, trans)) )
        try:
            oUser = currentThread().session.user
            iUserRole = objectAccess.getAccess(self, oUser)
            bCanMove = (iUserRole > objectAccess.AUTHOR)## or (iUserRole == objectAccess.AUTHOR and oItem.owner == oUser.id)

            parentId = self._parentid
            oTarget = db.getItem(targetId, trans)
        
            iUserRole2 = objectAccess.getAccess(oTarget, oUser)
        
            if self.isCollection and oTarget.isContainedIn(self._id):
                raise serverExceptions.TargetContainedInSource
        
            if (not(self._isSystem) and bCanMove and iUserRole2 > objectAccess.READER):
                if not(self.getContentclass() in oTarget.containment):
                    raise serverExceptions.ContainmentError

                self._parentid = targetId
                self.inheritRoles = False
                db.putItem(self, trans)

                #update target
                oTarget._addItemReference(self)
                db.putItem(oTarget, trans)

                #update parent
                oParent = db.getItem(parentId, trans)
                oParent._removeItemReference(self)
                db.putItem(oParent, trans)
            else:
                raise serverExceptions.PermissionDenied
        
        except serverExceptions.DBTransactionIncomplete:
            trans.retry()
    sys.stdout.write('Creating root folder...')
    rootFolder = schemas.org.innoscript.common.RootFolder()
    rootFolder._id = ''
    rootFolder.description.value = 'Root Folder'
    rootFolder.displayName.value = 'Porcupine Server'
    rootFolder._isSystem = True
    rootFolder._owner = sOwner
    rootFolder.modifiedBy = sOwner
    rootFolder._created = ftime
    rootFolder.modified = ftime
    rootFolder._subfolders = {
        'Categories':'categories',
        'Administrative Tools':'admintools'
    }
    rootFolder.security = {'everyone':1, 'administrators':8}
    db.putItem(rootFolder, None)
    sys.stdout.write('[OK]\n')

    sys.stdout.write('Creating recycle bin...')
    rb = schemas.org.innoscript.common.RecycleBin()
    rb._id = 'rb'
    
    rb.description.value = 'Deleted items container'
    rb.displayName.value = 'Recycle Bin'
    rb._isSystem = True
    rb._owner = sOwner
    rb.modifiedBy = sOwner
    rb._created = ftime
    rb.modified = ftime
    rb.inheritRoles = False
    rb.security = {'administrators':8}