--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cubicweb/schemas/workflow.py Sat Jan 16 13:48:51 2016 +0100
@@ -0,0 +1,283 @@
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+"""workflow related schemas
+
+"""
+__docformat__ = "restructuredtext en"
+from cubicweb import _
+
+from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
+ SubjectRelation,
+ RichString, String, Int)
+from cubicweb.schema import RQLConstraint
+from cubicweb.schemas import (PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS,
+ RO_REL_PERMS)
+
+class Workflow(EntityType):
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
+
+ name = String(required=True, indexed=True, internationalizable=True,
+ maxsize=256)
+ description = RichString(default_format='text/rest',
+ description=_('semantic description of this workflow'))
+
+ workflow_of = SubjectRelation('CWEType', cardinality='+*',
+ description=_('entity types which may use this workflow'),
+ constraints=[RQLConstraint('O final FALSE')])
+
+ initial_state = SubjectRelation('State', cardinality='?*',
+ constraints=[RQLConstraint('O state_of S',
+ msg=_('state doesn\'t belong to this workflow'))],
+ description=_('initial state for this workflow'))
+
+
+class default_workflow(RelationType):
+ """default workflow for an entity type"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+
+ subject = 'CWEType'
+ object = 'Workflow'
+ cardinality = '?*'
+ constraints = [RQLConstraint('S final FALSE, O workflow_of S',
+ msg=_('workflow isn\'t a workflow for this type'))]
+
+
+class State(EntityType):
+ """used to associate simple states to an entity type and/or to define
+ workflows
+ """
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
+ __unique_together__ = [('name', 'state_of')]
+ name = String(required=True, indexed=True, internationalizable=True, maxsize=256)
+ description = RichString(default_format='text/rest',
+ description=_('semantic description of this state'))
+
+ # XXX should be on BaseTransition w/ AND/OR selectors when we will
+ # implements #345274
+ allowed_transition = SubjectRelation('BaseTransition', cardinality='**',
+ constraints=[RQLConstraint('S state_of WF, O transition_of WF',
+ msg=_('state and transition don\'t belong the the same workflow'))],
+ description=_('allowed transitions from this state'))
+ state_of = SubjectRelation('Workflow', cardinality='1*', composite='object', inlined=True,
+ description=_('workflow to which this state belongs'))
+
+
+class BaseTransition(EntityType):
+ """abstract base class for transitions"""
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
+ __unique_together__ = [('name', 'transition_of')]
+
+ name = String(required=True, indexed=True, internationalizable=True, maxsize=256)
+ type = String(vocabulary=(_('normal'), _('auto')), default='normal')
+ description = RichString(description=_('semantic description of this transition'))
+
+ transition_of = SubjectRelation('Workflow', cardinality='1*', composite='object', inlined=True,
+ description=_('workflow to which this transition belongs'))
+
+
+class require_group(RelationDefinition):
+ """group in which a user should be to be allowed to pass this transition"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ subject = 'BaseTransition'
+ object = 'CWGroup'
+
+
+class condition(RelationDefinition):
+ """a RQL expression which should return some results, else the transition
+ won't be available.
+
+ This query may use X and U variables that will respectivly represents the
+ current entity and the current user.
+ """
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ subject = 'BaseTransition'
+ object = 'RQLExpression'
+ cardinality = '*?'
+ composite = 'subject'
+
+
+class Transition(BaseTransition):
+ """use to define a transition from one or multiple states to a destination
+ states in workflow's definitions. Transition without destination state will
+ go back to the state from which we arrived to the current state.
+ """
+ __specializes_schema__ = True
+
+ destination_state = SubjectRelation(
+ 'State', cardinality='?*',
+ constraints=[RQLConstraint('S transition_of WF, O state_of WF',
+ msg=_('state and transition don\'t belong the the same workflow'))],
+ description=_('destination state for this transition'))
+
+
+class WorkflowTransition(BaseTransition):
+ """special transition allowing to go through a sub-workflow"""
+ __specializes_schema__ = True
+
+ subworkflow = SubjectRelation('Workflow', cardinality='1*',
+ constraints=[RQLConstraint('S transition_of WF, WF workflow_of ET, O workflow_of ET',
+ msg=_('subworkflow isn\'t a workflow for the same types as the transition\'s workflow'))]
+ )
+ # XXX use exit_of and inline it
+ subworkflow_exit = SubjectRelation('SubWorkflowExitPoint', cardinality='*1',
+ composite='subject')
+
+
+class SubWorkflowExitPoint(EntityType):
+ """define how we get out from a sub-workflow"""
+ subworkflow_state = SubjectRelation(
+ 'State', cardinality='1*',
+ constraints=[RQLConstraint('T subworkflow_exit S, T subworkflow WF, O state_of WF',
+ msg=_('exit state must be a subworkflow state'))],
+ description=_('subworkflow state'))
+ destination_state = SubjectRelation(
+ 'State', cardinality='?*',
+ constraints=[RQLConstraint('T subworkflow_exit S, T transition_of WF, O state_of WF',
+ msg=_('destination state must be in the same workflow as our parent transition'))],
+ description=_('destination state. No destination state means that transition '
+ 'should go back to the state from which we\'ve entered the '
+ 'subworkflow.'))
+
+
+class TrInfo(EntityType):
+ """workflow history item"""
+ # 'add' security actually done by hooks
+ __permissions__ = {
+ 'read': ('managers', 'users', 'guests',), # XXX U has_read_permission O ?
+ 'add': ('managers', 'users', 'guests',),
+ 'delete': (), # XXX should we allow managers to delete TrInfo?
+ 'update': ('managers', 'owners',),
+ }
+ # The unique_together constraint ensures that 2 repositories
+ # sharing the db won't be able to fire a transition simultaneously
+ # on the same entity tr_count is filled in the FireTransitionHook
+ # to the number of TrInfo attached to the entity on which we
+ # attempt to fire a transition. In other word, it contains the
+ # rank of the TrInfo for that entity, and the constraint says we
+ # cannot have 2 TrInfo with the same rank.
+ __unique_together__ = [('tr_count', 'wf_info_for')]
+ from_state = SubjectRelation('State', cardinality='1*', inlined=True)
+ to_state = SubjectRelation('State', cardinality='1*', inlined=True)
+ # make by_transition optional because we want to allow managers to set
+ # entity into an arbitrary state without having to respect wf transition
+ by_transition = SubjectRelation('BaseTransition', cardinality='?*')
+ comment = RichString(fulltextindexed=True, default_format='text/plain')
+ tr_count = Int(description='autocomputed attribute used to ensure transition coherency')
+ # get actor and date time using owned_by and creation_date
+
+class from_state(RelationType):
+ __permissions__ = RO_REL_PERMS.copy()
+ inlined = True
+
+class to_state(RelationType):
+ __permissions__ = RO_REL_PERMS.copy()
+ inlined = True
+
+class by_transition(RelationType):
+ # 'add' security actually done by hooks
+ __permissions__ = {
+ 'read': ('managers', 'users', 'guests',),
+ 'add': ('managers', 'users', 'guests',),
+ 'delete': (),
+ }
+ inlined = True
+
+
+class workflow_of(RelationType):
+ """link a workflow to one or more entity type"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+
+class state_of(RelationType):
+ """link a state to one or more workflow"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ inlined = True
+
+class transition_of(RelationType):
+ """link a transition to one or more workflow"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ inlined = True
+
+class destination_state(RelationType):
+ """destination state of a transition"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ inlined = True
+
+class allowed_transition(RelationType):
+ """allowed transitions from this state"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+
+class initial_state(RelationType):
+ """indicate which state should be used by default when an entity using
+ states is created
+ """
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ inlined = True
+
+
+class subworkflow(RelationType):
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ inlined = True
+
+class exit_point(RelationType):
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+
+class subworkflow_state(RelationType):
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+ inlined = True
+
+
+# "abstract" relations, set by WorkflowableEntityType ##########################
+
+class custom_workflow(RelationType):
+ """allow to set a specific workflow for an entity"""
+ __permissions__ = PUB_SYSTEM_REL_PERMS
+
+ cardinality = '?*'
+ constraints = [RQLConstraint('S is ET, O workflow_of ET',
+ msg=_('workflow isn\'t a workflow for this type'))]
+ object = 'Workflow'
+
+
+class wf_info_for(RelationType):
+ """link a transition information to its object"""
+ # 'add' security actually done by hooks
+ __permissions__ = {
+ 'read': ('managers', 'users', 'guests',),
+ 'add': ('managers', 'users', 'guests',),
+ 'delete': (),
+ }
+ inlined = True
+
+ cardinality = '1*'
+ composite = 'object'
+ fulltext_container = composite
+ subject = 'TrInfo'
+
+
+class in_state(RelationType):
+ """indicate the current state of an entity"""
+ __permissions__ = RO_REL_PERMS
+
+ # not inlined intentionnally since when using ldap sources, user'state
+ # has to be stored outside the CWUser table
+ inlined = False
+
+ cardinality = '1*'
+ constraints = [RQLConstraint('S is ET, O state_of WF, WF workflow_of ET',
+ msg=_('state doesn\'t apply to this entity\'s type'))]
+ object = 'State'