##############################################################################
#
# Copyright (c) 2001 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
# 
##############################################################################
""" Map CMF actions to icons, for ease of building icon-centric toolbars.

$Id: ActionIconsTool.py,v 1.2 2003/07/13 01:55:30 tseaver Exp $
"""

import os

from Globals import InitializeClass
from Globals import package_home
from AccessControl import ClassSecurityInfo
from OFS.SimpleItem import SimpleItem

from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.CMFCore.Expression import Expression
from Products.CMFCore.utils import UniqueObject

from permissions import View
from permissions import ManageSite

_wwwdir = os.path.join( package_home( globals() ), 'www' )

class ActionIcon( SimpleItem ):

    security = ClassSecurityInfo()

    _title = None           # Use the one supplied by the provider
    _priority = 0           # All animals are equal....
    _category = 'object'
    _action_id = 'view'
    _icon_expr_text = 'document_icon'

    def __init__( self
                , category
                , action_id
                , icon_expr_text=''
                , title=None
                , priority=0
                ):

        self._category = category
        self._action_id = action_id
        self.updateIconExpression( icon_expr_text )
        self._title = title
        self._priority = priority

    security.declareProtected( View, 'getTitle' )
    def getTitle( self ):

        """ Simple accessor """
        return self._title

    security.declareProtected( View, 'getPriority' )
    def getPriority( self ):

        """ Simple accessor """
        return self._priority

    security.declareProtected( View, 'getCategory' )
    def getCategory( self ):

        """ Simple accessor """
        return self._category

    security.declareProtected( View, 'getActionId' )
    def getActionId( self ):

        """ Simple accessor """
        return self._action_id

    security.declareProtected( View, 'getExpression' )
    def getExpression( self ):

        """ Simple accessor """
        return self._icon_expr_text

    security.declareProtected( View, 'getIconURL' )
    def getIconURL( self, context=None ):

        """ Simple accessor """
        if context is None:
            return self._icon_expr_text

        return self._icon_expr( context )

    security.declareProtected( ManageSite, 'updateIconExpression' )
    def updateIconExpression( self, icon_expr_text ):

        """ Mutate icon expression. """
        self._icon_expr_text = icon_expr_text

        if not ':' in icon_expr_text: # default to 'string:' type
            icon_expr_text = 'string:%s' % icon_expr_text 

        self._icon_expr = Expression( icon_expr_text )

InitializeClass( ActionIcon )


class ActionIconsTool( UniqueObject, SimpleItem ):

    """ Map actions only icons.
    """
    meta_type = 'Action Icons Tool'
    id = 'portal_actionicons'

    security = ClassSecurityInfo()
    security.declareObjectProtected( View )

    def __init__( self ):

        self.clearActionIcons()

    #
    #   Accessors
    #
    security.declareProtected( ManageSite, 'listActionIcons' )
    def listActionIcons( self ):

        """ Return a sequence of mappings for action icons
        
        o Mappings are in the form: ( category, action ) -> icon,
          where category and action are strings and icon is an ActionIcon
          instance.
        """
        return [ x.__of__( self ) for x in self._icons ]

    security.declareProtected( View, 'getActionInfo' )
    def getActionInfo( self
                     , category
                     , action_id
                     , context=None
                     ):

        """ Return a tuple, '(title, priority, icon ID), for the given action.

        o Raise a KeyError if no icon has been defined for the action.
        """
        ai = self._lookup[ ( category, action_id ) ]
        return ( ai.getTitle()
               , ai.getPriority()
               , ai.getIconURL( context ) 
               )

    security.declareProtected( View, 'queryActionInfo' )
    def queryActionInfo( self
                       , category
                       , action_id
                       , default=None
                       , context=None
                       ):

        """ Return a tuple, '(title, priority, icon ID), for the given action.

        o Return 'default' if no icon has been defined for the action.
        """
        ai = self._lookup.get( ( category, action_id ) )
        return ai and ( ai.getTitle()
                      , ai.getPriority()
                      , ai.getIconURL( context ) 
                      ) or default

    security.declareProtected( View, 'getActionIcon' )
    def getActionIcon( self, category, action_id, context=None ):

        """ Return an icon ID for the given action.

        o Raise a KeyError if no icon has been defined for the action.

        o Context is an Expression context object, used to evaluate
          TALES expressions.
        """
        return self._lookup[ ( category, action_id ) ].getIconURL( context ) 

    security.declareProtected( View, 'queryActionIcon' )
    def queryActionIcon( self, category, action_id
                       , default=None, context=None ):

        """ Return an icon ID for the given action.

        o Return 'default' if no icon has been defined for the action.

        o Context is an Expression context object, used to evaluate
          TALES expressions.
        """
        ai = self._lookup.get( ( category, action_id ) )
        return ai and ai.getIconURL( context ) or default

    security.declareProtected( View, 'updateActionDicts' )
    def updateActionDicts( self, categorized_actions, context=None ):

        """ Update a set of dictionaries, adding 'title, 'priority', and
            'icon' keys.

        o S.b. passed a data structure like that returned from ActionsTool's
          'listFilteredActionsFor':
          
          - Dict mapping category -> seq. of dicts, where each of the
            leaf dicts must have 'category' and 'id' keys.

        o *Will* overwrite the 'title' key, if title is defined on the tool.

        o *Will* overwrite the 'priority' key.

        o *Will* overwrite the 'icon' key, if icon is defined on the tool

        o XXX:  Don't have a way to pass Expression context yet.
        """
        result = {}

        for category, actions in categorized_actions.items():

            new_actions = []

            for action in actions:

                action = action.copy()

                action_id = action.get( 'id' )

                #  Hack around DCWorkflow's ID-less worklist actions.
                if action_id is None and action.get( 'category' ) == 'workflow':
                    action[ 'id' ] = action_id = action.get( 'name' )

                if action_id:

                    info = self.queryActionInfo( category
                                               , action_id
                                               , context=context
                                               )
                    if info is not None:

                        title, priority, icon = info

                        if title is not None:
                            action[ 'title' ] = title

                        if priority is not None:
                            action[ 'priority' ] = priority

                        if icon is not None:
                            action[ 'icon' ] = icon

                new_actions.append( action )

            new_actions.sort( lambda x, y: cmp( x.get( 'priority', 0 )
                                              , y.get( 'priority', 0 )
                                              ) )
            result[ category ] = new_actions

        return result

    __call__ = updateActionDicts

    #
    #   Mutators
    #
    security.declareProtected( ManageSite, 'addActionIcon' )
    def addActionIcon( self
                     , category
                     , action_id
                     , icon_expr
                     , title=None
                     , priority=0
                     ):

        """ Add an icon for the given action.

        o Raise KeyError if an icon has already been defined.
        """
        if self.queryActionInfo( category, action_id ) is not None:
            raise KeyError, 'Duplicate definition!'

        icons = list( self._icons )
        icons.append( ActionIcon( category
                                , action_id
                                , icon_expr
                                , title
                                , priority
                                ) )
        self._lookup[ ( category, action_id ) ] = icons[-1]
        self._icons = tuple( icons )

    security.declareProtected( ManageSite, 'updateActionIcon' )
    def updateActionIcon( self
                        , category
                        , action_id
                        , icon_expr
                        , title=None
                        , priority=0
                        ):

        """ Update the icon for the given action.

        o Raise KeyError if an icon has not already been defined.
        """
        if self.queryActionIcon( category, action_id ) is None:
            raise KeyError, 'No such definition!'

        icons = list( self._icons )
        for ai in icons:
            if ( ai.getCategory() == category
             and ai.getActionId() == action_id
               ):
                ai.updateIconExpression( icon_expr )
                ai._title = title
                ai._priority = priority
                break
        else:
            raise KeyError, ( category, action_id )
        self._icons = tuple( icons )

    security.declareProtected( ManageSite, 'removeActionIcon' )
    def removeActionIcon( self, category, action_id ):

        """ Remove the icon for the given action.

        o Raise KeyError if an icon has not already been defined.
        """
        if self.queryActionInfo( category, action_id ) is None:
            raise KeyError, 'No such definition (%s, %s)!' % (
                category, action_id)

        icons = list( self._icons )
        icon = self._lookup[ ( category, action_id ) ]
        icons.remove( icon )
        del self._lookup[ ( category, action_id ) ]
        self._icons = tuple( icons )

    security.declareProtected( ManageSite, 'clearActionIcons' )
    def clearActionIcons( self ):

        """ Remove all mappings from the tool.
        """
        self._icons = ()
        self._lookup = {}

    #
    #   ZMI
    #
    manage_options =  ( { 'label' : 'Icons'
                        , 'action' : 'manage_editActionIcons'
                        }
                      ,
                      ) + SimpleItem.manage_options

    security.declareProtected( ManageSite, 'manage_editActionIcons' )
    manage_editActionIcons = PageTemplateFile( 'aitEdit', _wwwdir )

    security.declareProtected( ManageSite, 'manage_addActionIcon' )
    def manage_addActionIcon( self
                            , category
                            , action_id
                            , icon_expr
                            , title
                            , priority
                            , REQUEST
                            ):

        """ Add an icon for the given action via the ZMI.
        """
        self.addActionIcon( category
                          , action_id
                          , icon_expr
                          , title
                          , priority
                          )

        REQUEST['RESPONSE'].redirect( '%s/manage_editActionIcons'
                                      '?manage_tabs_message=Action+added.'
                                    % self.absolute_url()
                                    )

    security.declareProtected( ManageSite, 'manage_updateActionIcon' )
    def manage_updateActionIcon( self
                               , category
                               , action_id
                               , icon_expr
                               , title
                               , priority
                               , REQUEST
                               ):

        """ Update an icon for the given action via the ZMI.
        """
        self.updateActionIcon( category
                             , action_id
                             , icon_expr
                             , title
                             , priority
                             )

        REQUEST['RESPONSE'].redirect( '%s/manage_editActionIcons'
                                      '?manage_tabs_message=Action+updated.'
                                    % self.absolute_url()
                                    )

    security.declareProtected( ManageSite, 'manage_removeActionIcon' )
    def manage_removeActionIcon( self, category, action_id, REQUEST ):

        """ Remove the icon for the given action via the ZMI.
        """
        self.removeActionIcon( category, action_id )

        REQUEST['RESPONSE'].redirect( '%s/manage_editActionIcons'
                                      '?manage_tabs_message=Action+removed.'
                                    % self.absolute_url()
                                    )

InitializeClass( ActionIconsTool )
