from BaseFormAction import BaseFormAction, registerFormAction
import TraverseTo
from Products.CMFCore.utils import getToolByName
from urlparse import urlsplit

def factory(arg):
    """Create a new traverse-to-action action"""
    return TraverseToAction(arg)


class TraverseToAction(BaseFormAction):

    def __call__(self, controller_state):
        action = self.getArg(controller_state)
        action_url = None
        haveAction = False

        context = controller_state.getContext()
        actions_tool = getToolByName(context, 'portal_actions')
        fti = context.getTypeInfo()
        REQUEST = getattr(context, 'REQUEST', None)

        try:
            # Test to see if the action is defined in the FTI as an object or
            # folder action
            action_ob = fti.getActionObject('object/'+action)
            if action_ob is None:
                action_ob = fti.getActionObject('folder/'+action)
            # Use portal actions here so we have a full expression context
            ec = actions_tool._getExprContext(context)
            actiondict = action_ob.getAction(ec)
            haveAction = True
        except (ValueError, AttributeError):
            actions = actions_tool.listFilteredActionsFor(
                                                controller_state.getContext())
            # flatten the actions as we don't care where they are
            actions = reduce(lambda x,y,a=actions:  x+a[y], actions.keys(), [])
            for actiondict in actions:
                if actiondict['id'] == action:
                    haveAction = True
                    break
        # For traversal, our 'url' must be a traversable path
        if haveAction:
            action_url = actiondict['url'].strip()
            url_parts = urlsplit(action_url)
            # Check to see if we have a protocol, if so convert to a path,
            # otherwise make the assumption that we are dealing with a
            # physical path
            if url_parts[0] and REQUEST is not None:
                action_url = '/'.join(REQUEST.physicalPathFromURL(action_url))
            else:
                action_url = url_parts[2]
        else:
            raise ValueError, 'No %s action found for %s' % (action, controller_state.getContext().getId())

        # If we have CMF 1.5, the actual action_url may be hidden behind a method
        # alias. Attempt to resolve this
        try:
            if action_url:
                # If our url is a path, then we need to see if it contains the
                # path to the current object, if so we need to check if the
                # remaining path element is a method alias
                possible_alias = action_url
                current_path = '/'.join(context.getPhysicalPath())
                if possible_alias.startswith(current_path):
                    possible_alias = possible_alias[len(current_path)+1:]
                if possible_alias:
                    action_url = fti.queryMethodID(possible_alias,
                                                   default = action_url,
                                                   context = context)
        except AttributeError:
            # Don't raise if we don't have CMF 1.5
            pass

        # XXX: Is there a better way to check this?
        if not action_url.startswith('string:'):
            action_url = 'string:%s' % (action_url,)
        return TraverseTo.TraverseTo(action_url)(controller_state)

registerFormAction('traverse_to_action',
                   factory,
                   'Traverse to the action specified in the argument (a TALES expression) for the current context object (e.g. string:view)')
