add state-dependent control of actions and a doBefore handler for transitions
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@3240 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
1fa202c5c2
commit
35f9224c25
3 changed files with 89 additions and 23 deletions
|
@ -142,5 +142,3 @@ class TransitionEvent(ObjectEvent):
|
|||
self.transition = transition
|
||||
self.previousState = previousState
|
||||
self.request = request
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ $Id$
|
|||
from zope.interface import implements
|
||||
from cybertools.util.jeep import Jeep
|
||||
|
||||
from cybertools.stateful.interfaces import IState, ITransition
|
||||
from cybertools.stateful.interfaces import IState, IAction, ITransition
|
||||
from cybertools.stateful.interfaces import IStatesDefinition
|
||||
|
||||
|
||||
|
@ -33,7 +33,7 @@ class State(object):
|
|||
|
||||
implements(IState)
|
||||
|
||||
security = lambda context: None
|
||||
setSecurity = lambda self, context: None
|
||||
icon = None
|
||||
color = 'blue'
|
||||
|
||||
|
@ -41,20 +41,37 @@ class State(object):
|
|||
self.name = self.__name__ = name
|
||||
self.title = title
|
||||
self.transitions = transitions
|
||||
self.actions = Jeep(kw.pop('actions', []))
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
class Transition(object):
|
||||
class Action(object):
|
||||
|
||||
implements(IAction)
|
||||
|
||||
allowed = True
|
||||
permission = None
|
||||
roles = []
|
||||
|
||||
def __init__(self, name, title=None, **kw):
|
||||
self.name = self.__name__ = name
|
||||
self.title = title
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
@staticmethod
|
||||
def doBefore(context):
|
||||
return None
|
||||
|
||||
|
||||
class Transition(Action):
|
||||
|
||||
implements(ITransition)
|
||||
|
||||
def __init__(self, name, title, targetState, **kw):
|
||||
self.name = self.__name__ = name
|
||||
self.title = title
|
||||
super(Transition, self).__init__(name, title, **kw)
|
||||
self.targetState = targetState
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
class StatesDefinition(object):
|
||||
|
@ -81,14 +98,36 @@ class StatesDefinition(object):
|
|||
def doTransitionFor(self, obj, transition):
|
||||
if transition not in self.transitions:
|
||||
raise ValueError('Transition %s is not available.' % transition)
|
||||
if transition not in [t.name for t in self.getAvailableTransitionsFor(obj)]:
|
||||
trans = self.transitions[transition]
|
||||
if not self.isAllowed(trans, obj):
|
||||
raise ValueError('Transition %s is not allowed.' % transition)
|
||||
if trans not in self.getAvailableTransitionsFor(obj):
|
||||
raise ValueError("Transition '%s' is not reachable from state '%s'."
|
||||
% (transition, obj.getState()))
|
||||
obj.state = self.transitions[transition].targetState
|
||||
trans.doBefore(obj)
|
||||
obj.state = trans.targetState
|
||||
obj.getStateObject().setSecurity(obj)
|
||||
|
||||
def getAvailableTransitionsFor(self, obj):
|
||||
state = obj.getState()
|
||||
return [self.transitions[t] for t in self.states[state].transitions]
|
||||
return [self.transitions[t]
|
||||
for t in self.states[state].transitions
|
||||
if self.isAllowed(self.transitions[t], obj)]
|
||||
|
||||
def isAllowed(self, action, obj):
|
||||
if not action.allowed:
|
||||
return False
|
||||
if not self.checkRoles(action.roles, obj):
|
||||
return False
|
||||
if not self.checkPermission(action.permission, obj):
|
||||
return False
|
||||
return True
|
||||
|
||||
def checkRoles(self, roles, obj):
|
||||
return True
|
||||
|
||||
def checkPermission(self, permission, obj):
|
||||
return True
|
||||
|
||||
|
||||
# dummy default states definition
|
||||
|
@ -101,6 +140,7 @@ defaultSD = StatesDefinition('default',
|
|||
|
||||
|
||||
# states definitions registry
|
||||
# TODO: use a utility!!!
|
||||
|
||||
statesDefinitions = dict()
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -33,18 +33,31 @@ class IState(Interface):
|
|||
|
||||
name = Attribute('The name or identifier of the state')
|
||||
title = Attribute('A user-readable name or title of the state')
|
||||
transitions = Attribute('A sequence of strings naming the transitions '
|
||||
'that can be executed from this state')
|
||||
security = Attribute('A callable setting the security settings for '
|
||||
'an object in this state when executed.')
|
||||
transitions = Attribute('A collection of strings naming the transitions '
|
||||
'that can be executed from this state.')
|
||||
actions = Attribute('A mapping with actions that may be executed in '
|
||||
'this state.')
|
||||
setSecurity = Attribute('A callable (argument: stateful object) '
|
||||
'for setting the security settings on the object.')
|
||||
|
||||
|
||||
class ITransition(Interface):
|
||||
class IAction(Interface):
|
||||
|
||||
name = Attribute('The name or identifier of the action.')
|
||||
title = Attribute('A user-readable name or title of the action.')
|
||||
allowed = Attribute('A boolean; if False the action may not be executed.')
|
||||
doBefore = Attribute('A callable (argument: stateful object) to be executed '
|
||||
'before this action.')
|
||||
roles = Attribute('A collection of names of the roles that are allowed '
|
||||
'to execute this action; no check when empty.')
|
||||
permission = Attribute('The name of a permission that is needed for '
|
||||
'executing this action; no check when empty.')
|
||||
|
||||
|
||||
class ITransition(IAction):
|
||||
|
||||
name = Attribute('The name or identifier of the transition')
|
||||
title = Attribute('A user-readable name or title of the transition')
|
||||
targetState = Attribute('A string naming the state that will be the '
|
||||
'result of executing this transition')
|
||||
'result of executing this transition.')
|
||||
|
||||
|
||||
class IStateful(Interface):
|
||||
|
@ -119,9 +132,24 @@ class IStatesDefinition(Interface):
|
|||
""" Return the transitions available for this object in its current state.
|
||||
"""
|
||||
|
||||
def isAllowed(action, object):
|
||||
""" Return True if the action (an IAction provider) is allowed on
|
||||
the object given for the current user.
|
||||
"""
|
||||
|
||||
def checkRoles(roles, object):
|
||||
""" Return True if the current user provides one of the roles given
|
||||
on the object given.
|
||||
"""
|
||||
|
||||
def checkRoles(permission, object):
|
||||
""" Return True if the current user has the permission given
|
||||
on the object given.
|
||||
"""
|
||||
|
||||
|
||||
class IStatefulIndexInfo(Interface):
|
||||
""" Provide a list of tokens to be used for index the states
|
||||
""" Provide a list of tokens to be used for indexing the states
|
||||
of an object in the catalog.
|
||||
"""
|
||||
|
||||
|
@ -133,7 +161,7 @@ class IStatefulIndexInfo(Interface):
|
|||
|
||||
|
||||
class ITransitionEvent(IObjectEvent):
|
||||
""" Fires when the state of an object is changed.
|
||||
""" Fires when the state of an object has been changed.
|
||||
"""
|
||||
|
||||
transition = Attribute('The transition.')
|
||||
|
|
Loading…
Add table
Reference in a new issue