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.transition = transition
|
||||||
self.previousState = previousState
|
self.previousState = previousState
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ $Id$
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from cybertools.util.jeep import Jeep
|
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
|
from cybertools.stateful.interfaces import IStatesDefinition
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class State(object):
|
||||||
|
|
||||||
implements(IState)
|
implements(IState)
|
||||||
|
|
||||||
security = lambda context: None
|
setSecurity = lambda self, context: None
|
||||||
icon = None
|
icon = None
|
||||||
color = 'blue'
|
color = 'blue'
|
||||||
|
|
||||||
|
@ -41,20 +41,37 @@ class State(object):
|
||||||
self.name = self.__name__ = name
|
self.name = self.__name__ = name
|
||||||
self.title = title
|
self.title = title
|
||||||
self.transitions = transitions
|
self.transitions = transitions
|
||||||
|
self.actions = Jeep(kw.pop('actions', []))
|
||||||
for k, v in kw.items():
|
for k, v in kw.items():
|
||||||
setattr(self, k, v)
|
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)
|
implements(ITransition)
|
||||||
|
|
||||||
def __init__(self, name, title, targetState, **kw):
|
def __init__(self, name, title, targetState, **kw):
|
||||||
self.name = self.__name__ = name
|
super(Transition, self).__init__(name, title, **kw)
|
||||||
self.title = title
|
|
||||||
self.targetState = targetState
|
self.targetState = targetState
|
||||||
for k, v in kw.items():
|
|
||||||
setattr(self, k, v)
|
|
||||||
|
|
||||||
|
|
||||||
class StatesDefinition(object):
|
class StatesDefinition(object):
|
||||||
|
@ -81,14 +98,36 @@ class StatesDefinition(object):
|
||||||
def doTransitionFor(self, obj, transition):
|
def doTransitionFor(self, obj, transition):
|
||||||
if transition not in self.transitions:
|
if transition not in self.transitions:
|
||||||
raise ValueError('Transition %s is not available.' % transition)
|
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'."
|
raise ValueError("Transition '%s' is not reachable from state '%s'."
|
||||||
% (transition, obj.getState()))
|
% (transition, obj.getState()))
|
||||||
obj.state = self.transitions[transition].targetState
|
trans.doBefore(obj)
|
||||||
|
obj.state = trans.targetState
|
||||||
|
obj.getStateObject().setSecurity(obj)
|
||||||
|
|
||||||
def getAvailableTransitionsFor(self, obj):
|
def getAvailableTransitionsFor(self, obj):
|
||||||
state = obj.getState()
|
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
|
# dummy default states definition
|
||||||
|
@ -101,6 +140,7 @@ defaultSD = StatesDefinition('default',
|
||||||
|
|
||||||
|
|
||||||
# states definitions registry
|
# states definitions registry
|
||||||
|
# TODO: use a utility!!!
|
||||||
|
|
||||||
statesDefinitions = dict()
|
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
|
# 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
|
# 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')
|
name = Attribute('The name or identifier of the state')
|
||||||
title = Attribute('A user-readable name or title of the state')
|
title = Attribute('A user-readable name or title of the state')
|
||||||
transitions = Attribute('A sequence of strings naming the transitions '
|
transitions = Attribute('A collection of strings naming the transitions '
|
||||||
'that can be executed from this state')
|
'that can be executed from this state.')
|
||||||
security = Attribute('A callable setting the security settings for '
|
actions = Attribute('A mapping with actions that may be executed in '
|
||||||
'an object in this state when executed.')
|
'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 '
|
targetState = Attribute('A string naming the state that will be the '
|
||||||
'result of executing this transition')
|
'result of executing this transition.')
|
||||||
|
|
||||||
|
|
||||||
class IStateful(Interface):
|
class IStateful(Interface):
|
||||||
|
@ -119,9 +132,24 @@ class IStatesDefinition(Interface):
|
||||||
""" Return the transitions available for this object in its current state.
|
""" 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):
|
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.
|
of an object in the catalog.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -133,7 +161,7 @@ class IStatefulIndexInfo(Interface):
|
||||||
|
|
||||||
|
|
||||||
class ITransitionEvent(IObjectEvent):
|
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.')
|
transition = Attribute('The transition.')
|
||||||
|
|
Loading…
Add table
Reference in a new issue