[Zope-Checkins] CVS: Zope/lib/python/OFS - IOrderSupport.py:1.1.2.1 OrderSupport.py:1.1.2.1 OrderedFolder.py:1.1.2.1

Yvo Schubbe schubbe@web.de
Fri, 25 Apr 2003 09:07:22 -0400


Update of /cvs-repository/Zope/lib/python/OFS
In directory cvs.zope.org:/tmp/cvs-serv1454/OFS

Added Files:
      Tag: yuppie-ordersupport-branch
	IOrderSupport.py OrderSupport.py OrderedFolder.py 
Log Message:
initial checkin of OrderSupport and OrderedFolder


=== Added File Zope/lib/python/OFS/IOrderSupport.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
""" Order support interfaces.

$Id: IOrderSupport.py,v 1.1.2.1 2003/04/25 13:07:21 yuppie Exp $
"""

from Interface import Interface


class IOrderedContainer(Interface):
    """ Ordered Container interface.

    This interface provides a common mechanism for maintaining ordered
    collections.
    """

    def moveObjectsByDelta(ids, delta):
        """ Move specified sub-objects by delta.

        If delta is higher than the possible maximum, objects will be moved to
        the bottom. If delta is lower than the possible minimum, objects will
        be moved to the top.

        The order of the objects specified by ids will always be preserved. If
        an object with id doesn't exist an error will be raised.

        Permission -- Manage properties

        Returns -- Number of moved sub-objects
        """

    def moveObjectsUp(ids, delta=1):
        """ Move specified sub-objects up by delta in container.
        
        If no delta is specified, delta is 1. See moveObjectsByDelta for more
        details.

        Permission -- Manage properties

        Returns -- Number of moved sub-objects
        """

    def moveObjectsDown(ids, delta=1):
        """ Move specified sub-objects down by delta in container.

        If no delta is specified, delta is 1. See moveObjectsByDelta for more
        details.

        Permission -- Manage properties

        Returns -- Number of moved sub-objects
        """

    def moveObjectsToTop(ids):
        """ Move specified sub-objects to top of container.

        See moveObjectsByDelta for more details.

        Permission -- Manage properties

        Returns -- Number of moved sub-objects
        """

    def moveObjectsToBottom(ids):
        """ Move specified sub-objects to bottom of container.

        See moveObjectsByDelta for more details.

        Permission -- Manage properties

        Returns -- Number of moved sub-objects
        """

    def orderObjects(key, reverse=None):
        """ Order sub-objects by key and direction.

        Permission -- Manage properties

        Returns -- Number of moved sub-objects
        """

    def getObjectPosition(id):
        """ Get the position of an object by its id.

        Permission -- Access contents information

        Returns -- Position
        """

    def moveObjectToPosition(id, position):
        """ Moves specified object to absolute position.

        Permission -- Manage properties

        Returns -- Number of moved sub-objects
        """


=== Added File Zope/lib/python/OFS/OrderSupport.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
""" Order support for 'Object Manager'.

$Id: OrderSupport.py,v 1.1.2.1 2003/04/25 13:07:21 yuppie Exp $
"""

from types import StringType

from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information
from AccessControl.Permissions import manage_properties
from DocumentTemplate.sequence import sort
from Globals import InitializeClass

from IOrderSupport import IOrderedContainer
from ObjectManager import ObjectManager


class OrderSupport:
    """ Ordered container mixin class - This is an extension to the regular
    ObjectManager. It saves the objects in order and lets you change the order
    of the contained objects. This is particular helpful, if the order does
    not depend on object attributes, but is totally user-specific.
    """

    __implements__ = IOrderedContainer
    security = ClassSecurityInfo()

    has_order_support = 1
    _key = 'position'
    _reverse = 0

    manage_options = ( { 'label':'Contents',
                         'action':'manage_main',
                         'help':('OFSP','OrderSupport_Contents.stx') }
                     ,
                     )

    #
    #   ZMI Methods
    #

    security.declareProtected(manage_properties, 'manage_move_objects_up')
    def manage_move_objects_up(self, REQUEST, ids=None, delta=None):
        """ Move specified sub-objects up by delta in container.
        """
        if ids:
            try:
                attempt = self.moveObjectsUp(ids, delta)
                message = '%d item%s moved up.' % ( attempt,
                                              ( (attempt!=1) and 's' or '' ) )
            except ValueError, errmsg:
                message = 'Error: %s' % (errmsg)
        else:
            message = 'Error: No items were specified!'
        return self.manage_main(self, REQUEST, skey='position',
                                manage_tabs_message=message)

    security.declareProtected(manage_properties, 'manage_move_objects_down')
    def manage_move_objects_down(self, REQUEST, ids=None, delta=None):
        """ Move specified sub-objects down by delta in container.
        """
        if ids:
            try:
                attempt = self.moveObjectsDown(ids, delta)
                message = '%d item%s moved down.' % ( attempt,
                                              ( (attempt!=1) and 's' or '' ) )
            except ValueError, errmsg:
                message = 'Error: %s' % (errmsg)
        else:
            message = 'Error: No items were specified!'
        return self.manage_main(self, REQUEST, skey='position',
                                manage_tabs_message=message)

    security.declareProtected(manage_properties, 'manage_move_objects_to_top')
    def manage_move_objects_to_top(self, REQUEST, ids=None):
        """ Move specified sub-objects to top of container.
        """
        if ids:
            try:
                attempt = self.moveObjectsToTop(ids)
                message = '%d item%s moved to top.' % ( attempt,
                                              ( (attempt!=1) and 's' or '' ) )
            except ValueError, errmsg:
                message = 'Error: %s' % (errmsg)
        else:
            message = 'Error: No items were specified!'
        return self.manage_main(self, REQUEST, skey='position',
                                manage_tabs_message=message)

    security.declareProtected(manage_properties, 'manage_move_objects_to_bottom')
    def manage_move_objects_to_bottom(self, REQUEST, ids=None):
        """ Move specified sub-objects to bottom of container.
        """
        if ids:
            try:
                attempt = self.moveObjectsToBottom(ids)
                message = '%d item%s moved to bottom.' % ( attempt,
                                              ( (attempt!=1) and 's' or '' ) )
            except ValueError, errmsg:
                message = 'Error: %s' % (errmsg)
        else:
            message = 'Error: No items were specified!'
        return self.manage_main(self, REQUEST, skey='position',
                                manage_tabs_message=message)

    security.declareProtected(manage_properties, 'manage_apply_sorting')
    def manage_apply_sorting(self, REQUEST, key, reverse):
        """ Order objects by sort_key, ascending or descending.
        """
        message = ''
        if key != 'position':
            self.orderObjects(key, reverse)
            message += 'Ordered objects by %s (%s). ' % (key,
                                      reverse and 'descending' or 'ascending')
        self.setDefaultSorting(key, reverse)
        message += 'Current sorting is now default.'

        return self.manage_main(self, REQUEST, manage_tabs_message=message)


    #
    #   IOrderedContainer Interface Methods
    #

    security.declareProtected(manage_properties, 'moveObjectsByDelta')
    def moveObjectsByDelta(self, ids, delta):
        """ Move specified sub-objects by delta.
        """
        if type(ids) is StringType:
            ids = (ids,)
        min_position = 0
        objects = list(self._objects)
        obj_dict = {}
        for obj in self._objects:
            obj_dict[ obj['id'] ] = obj
        if delta > 0:
            ids = list(ids)
            ids.reverse()
            objects.reverse()
        counter = 0

        for id in ids:
            try:
                object = obj_dict[id]
            except KeyError:
                raise (ValueError,
                       'The object with the id "%s" does not exist.' % id)
            old_position = objects.index(object)
            new_position = max( old_position - abs(delta), min_position )
            if new_position == min_position:
                min_position += 1
            if not old_position == new_position:
                objects.remove(object)
                objects.insert(new_position, object)
                counter += 1

        if counter > 0:
            if delta > 0:
                objects.reverse()
            self._objects = tuple(objects)

        return counter

    security.declareProtected(manage_properties, 'moveObjectsUp')
    def moveObjectsUp(self, ids, delta=1):
        """ Move specified sub-objects up by delta in container.
        """
        return self.moveObjectsByDelta(ids, -delta)

    security.declareProtected(manage_properties, 'moveObjectsDown')
    def moveObjectsDown(self, ids, delta=1):
        """ Move specified sub-objects down by delta in container.
        """
        return self.moveObjectsByDelta(ids, delta)

    security.declareProtected(manage_properties, 'moveObjectsToTop')
    def moveObjectsToTop(self, ids):
        """ Move specified sub-objects to top of container.
        """
        return self.moveObjectsByDelta( ids, -len(self._objects) )

    security.declareProtected(manage_properties, 'moveObjectsToBottom')
    def moveObjectsToBottom(self, ids):
        """ Move specified sub-objects to bottom of container.
        """
        return self.moveObjectsByDelta( ids, len(self._objects) )

    security.declareProtected(manage_properties, 'orderObjects')
    def orderObjects(self, key, reverse=None):
        """ Order sub-objects by key and direction.
        """
        ids = [ id for id, obj in sort( self.objectItems(),
                                        ( (key, 'cmp', 'asc'), ) ) ]
        if reverse:
            ids.reverse()
        return self.moveObjectsByDelta( ids, -len(self._objects) )

    security.declareProtected(access_contents_information,
                              'getObjectPosition')
    def getObjectPosition(self, id):
        """ Get the position of an object by its id.
        """
        ids = self.objectIds()
        if id in ids:
            return ids.index(id)
        raise ValueError, 'The object with the id "%s" does not exist.' % id

    security.declareProtected(manage_properties, 'moveObjectToPosition')
    def moveObjectToPosition(self, id, position):
        """ Move specified object to absolute position.
        """        
        delta = position - self.getObjectPosition(id)
        return self.moveObjectsByDelta(id, delta)

    security.declareProtected(access_contents_information, 'getDefaultSorting')
    def getDefaultSorting(self):
        """ Get default sorting key and direction.
        """
        return self._key, self._reverse

    security.declareProtected(manage_properties, 'setDefaultSorting')
    def setDefaultSorting(self, key, reverse):
        """ Set default sorting key and direction.
        """
        self._key = key
        self._reverse = reverse and 1 or 0


    #
    #   Override Inherited Method of ObjectManager Subclass
    #

    _old_manage_renameObject = ObjectManager.inheritedAttribute(
                                                        'manage_renameObject')
    def manage_renameObject(self, id, new_id, REQUEST=None):
        """ Rename a particular sub-object without changing its position.
        """
        old_position = self.getObjectPosition(id)
        result = self._old_manage_renameObject(id, new_id, REQUEST)
        self.moveObjectToPosition(new_id, old_position)
        return result

InitializeClass(OrderSupport)


=== Added File Zope/lib/python/OFS/OrderedFolder.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
""" 'Folder' with order support.

$Id: OrderedFolder.py,v 1.1.2.1 2003/04/25 13:07:21 yuppie Exp $
"""

from AccessControl import getSecurityManager
from AccessControl import Unauthorized
from AccessControl.Permissions import add_page_templates
from AccessControl.Permissions import add_user_folders
from Globals import DTMLFile

from Folder import Folder
from OrderSupport import OrderSupport

manage_addOrderedFolderForm = DTMLFile('dtml/addOrderedFolder', globals())

def manage_addOrderedFolder(self, id, title='', createPublic=0, createUserF=0,
                            REQUEST=None):
    """Add a new ordered Folder object with id *id*.

    If the 'createPublic' and 'createUserF' parameters are set to any true
    value, an 'index_html' and a 'UserFolder' objects are created respectively
    in the new folder.
    """
    ob = OrderedFolder()
    ob.id = str(id)
    ob.title = title
    self._setObject(id, ob)
    ob = self._getOb(id)

    checkPermission = getSecurityManager().checkPermission

    if createUserF:
        if not checkPermission(add_user_folders, ob):
            raise Unauthorized, (
                  'You are not authorized to add User Folders.'
                  )
        ob.manage_addUserFolder()

    if createPublic:
        if not checkPermission(add_page_templates, ob):
            raise Unauthorized, (
                  'You are not authorized to add Page Templates.'
                  )
        ob.manage_addProduct['PageTemplates'].manage_addPageTemplate(
            id='index_html', title='')

    if REQUEST:
        return self.manage_main(self, REQUEST, update_menu=1)


class OrderedFolder(OrderSupport, Folder):
    """ Extends the default Folder by order support.
    """
    meta_type='Folder (Ordered)'
    __implements__ = (OrderSupport.__implements__,
                      Folder.__implements__)

    manage_options = ( OrderSupport.manage_options +
                       Folder.manage_options[1:] )