src/pyams_workflow/interfaces/__init__.py
changeset 0 73acbfc13577
child 2 6acf23529fc2
equal deleted inserted replaced
-1:000000000000 0:73acbfc13577
       
     1 #
       
     2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 __docformat__ = 'restructuredtext'
       
    14 
       
    15 
       
    16 # import standard library
       
    17 
       
    18 # import interfaces
       
    19 from zope.annotation.interfaces import IAttributeAnnotatable
       
    20 from zope.interface.interfaces import IObjectEvent, ObjectEvent
       
    21 
       
    22 # import packages
       
    23 from pyams_security.schema import Principal
       
    24 from zope.interface import implementer, invariant, Interface, Attribute, Invalid
       
    25 from zope.schema import Choice, Datetime, Set, TextLine, Text, List, Object, Int
       
    26 
       
    27 from pyams_workflow import _
       
    28 
       
    29 
       
    30 MANUAL = 0
       
    31 AUTOMATIC = 1
       
    32 SYSTEM = 2
       
    33 
       
    34 
       
    35 class InvalidTransitionError(Exception):
       
    36     """Base transition error"""
       
    37 
       
    38     def __init__(self, source):
       
    39         self.source = source
       
    40 
       
    41     def __str__(self):
       
    42         return 'source: "%s"' % self.source
       
    43 
       
    44 
       
    45 class NoTransitionAvailableError(InvalidTransitionError):
       
    46     """Exception raised when there is not available transition"""
       
    47 
       
    48     def __init__(self, source, destination):
       
    49         super(NoTransitionAvailableError, self).__init__(source)
       
    50         self.destination = destination
       
    51 
       
    52     def __str__(self):
       
    53         return 'source: "%s" destination: "%s"' % (self.source, self.destination)
       
    54 
       
    55 
       
    56 class AmbiguousTransitionError(InvalidTransitionError):
       
    57     """Exception raised when required transition is ambiguous"""
       
    58 
       
    59     def __init__(self, source, destination):
       
    60         super(AmbiguousTransitionError, self).__init__(source)
       
    61         self.destination = destination
       
    62 
       
    63     def __str__(self):
       
    64         return 'source: "%s" destination: "%s"' % (self.source, self.destination)
       
    65 
       
    66 
       
    67 class VersionError(Exception):
       
    68     """Versions management error"""
       
    69 
       
    70 
       
    71 class ConditionFailedError(Exception):
       
    72     """Exception raised when transition condition failed"""
       
    73 
       
    74 
       
    75 class IWorkflowTransitionEvent(IObjectEvent):
       
    76     """Workflow transition event interface"""
       
    77 
       
    78     source = Attribute('Original state or None if initial state')
       
    79 
       
    80     destination = Attribute('New state')
       
    81 
       
    82     transition = Attribute('Transition that was fired or None if initial state')
       
    83 
       
    84     comment = Attribute('Comment that went with state transition')
       
    85 
       
    86 
       
    87 @implementer(IWorkflowTransitionEvent)
       
    88 class WorkflowTransitionEvent(ObjectEvent):
       
    89     """Workflow transition event"""
       
    90 
       
    91     def __init__(self, object, source, destination, transition, comment):
       
    92         super(WorkflowTransitionEvent, self).__init__(object)
       
    93         self.source = source
       
    94         self.destination = destination
       
    95         self.transition = transition
       
    96         self.comment = comment
       
    97 
       
    98 
       
    99 class IWorkflowVersionTransitionEvent(IWorkflowTransitionEvent):
       
   100     """Workflow version transition event interface"""
       
   101 
       
   102     old_object = Attribute('Old version of object')
       
   103 
       
   104 
       
   105 @implementer(IWorkflowVersionTransitionEvent)
       
   106 class WorkflowVersionTransitionEvent(WorkflowTransitionEvent):
       
   107     """Workflow version transition event"""
       
   108 
       
   109     def __init__(self, object, old_object, source, destination, transition, comment):
       
   110         super(WorkflowVersionTransitionEvent, self).__init__(object, source, destination, transition, comment)
       
   111         self.old_object = old_object
       
   112 
       
   113 
       
   114 class IWorkflow(Interface):
       
   115     """Defines workflow in the form of transition objects.
       
   116 
       
   117     Defined as a utility.
       
   118     """
       
   119 
       
   120     published_states = Set(title="Published states")
       
   121 
       
   122     def initialize(self):
       
   123         """Do any needed initialization.
       
   124 
       
   125         Such as initialization with the workflow versions system.
       
   126         """
       
   127 
       
   128     def refresh(self, transitions):
       
   129         """Refresh workflow completely with new transitions."""
       
   130 
       
   131     def get_transitions(self, source):
       
   132         """Get all transitions from source"""
       
   133 
       
   134     def get_transition(self, source, transition_id):
       
   135         """Get transition with transition_id given source state.
       
   136 
       
   137         If the transition is invalid from this source state,
       
   138         an InvalidTransitionError is raised.
       
   139         """
       
   140 
       
   141     def get_transition_by_id(self, transition_id):
       
   142         """Get transition with transition_id"""
       
   143 
       
   144 
       
   145 class IWorkflowInfo(Interface):
       
   146     """Get workflow info about workflowed object, and drive workflow.
       
   147 
       
   148     Defined as an adapter.
       
   149     """
       
   150 
       
   151     def set_initial_state(self, state, comment=None):
       
   152         """Set initial state for the context object.
       
   153 
       
   154         Fires a transition event.
       
   155         """
       
   156 
       
   157     def fire_transition(self, transition_id, comment=None, side_effect=None, check_security=True):
       
   158         """Fire a transition for the context object.
       
   159 
       
   160         There's an optional comment parameter that contains some
       
   161         opaque object that offers a comment about the transition.
       
   162         This is useful for manual transitions where users can motivate
       
   163         their actions.
       
   164 
       
   165         There's also an optional side effect parameter which should
       
   166         be a callable which receives the object undergoing the transition
       
   167         as the parameter. This could do an editing action of the newly
       
   168         transitioned workflow object before an actual transition event is
       
   169         fired.
       
   170 
       
   171         If check_security is set to False, security is not checked
       
   172         and an application can fire a transition no matter what the
       
   173         user's permission is.
       
   174         """
       
   175 
       
   176     def fire_transition_toward(self, state, comment=None, side_effect=None, check_security=True):
       
   177         """Fire transition toward state.
       
   178 
       
   179         Looks up a manual transition that will get to the indicated
       
   180         state.
       
   181 
       
   182         If no such transition is possible, NoTransitionAvailableError will
       
   183         be raised.
       
   184 
       
   185         If more than one manual transitions are possible,
       
   186         AmbiguousTransitionError will be raised.
       
   187         """
       
   188 
       
   189     def fire_transition_for_versions(self, state, transition_id, comment=None):
       
   190         """Fire a transition for all versions in a state"""
       
   191 
       
   192     def fire_automatic(self):
       
   193         """Fire automatic transitions if possible by condition"""
       
   194 
       
   195     def has_version(self, state):
       
   196         """Return true if a version exists in given state"""
       
   197 
       
   198     def get_manual_transition_ids(self):
       
   199         """Returns list of valid manual transitions.
       
   200 
       
   201         These transitions have to have a condition that's True.
       
   202         """
       
   203 
       
   204     def get_manual_transition_ids_toward(self, state):
       
   205         """Returns list of manual transitions towards state"""
       
   206 
       
   207     def get_automatic_transition_ids(self):
       
   208         """Returns list of possible automatic transitions.
       
   209 
       
   210         Condition is not checked.
       
   211         """
       
   212 
       
   213     def has_automatic_transitions(self):
       
   214         """Return true if there are possible automatic outgoing transitions.
       
   215 
       
   216         Condition is not checked.
       
   217         """
       
   218 
       
   219 
       
   220 class IWorkflowStateHistoryItem(Interface):
       
   221     """Workflow state history item"""
       
   222 
       
   223     date = Datetime(title="State change datetime",
       
   224                     required=True)
       
   225 
       
   226     source_version = Int(title="Source version ID",
       
   227                          required=False)
       
   228 
       
   229     source_state = TextLine(title="Transition source state",
       
   230                             required=False)
       
   231 
       
   232     target_state = TextLine(title="Transition target state",
       
   233                             required=True)
       
   234 
       
   235     transition = TextLine(title="Transition name",
       
   236                           required=True)
       
   237 
       
   238     principal = Principal(title="Transition principal",
       
   239                           required=False)
       
   240 
       
   241     comment = Text(title="Transition comment",
       
   242                    required=False)
       
   243 
       
   244 
       
   245 class IWorkflowState(Interface):
       
   246     """Store state on workflowed objects.
       
   247 
       
   248     Defined as an adapter.
       
   249     """
       
   250 
       
   251     version_id = Attribute("Version ID")
       
   252 
       
   253     state = Attribute("Version state")
       
   254 
       
   255     history = List(title="Workflow states history",
       
   256                    value_type=Object(schema=IWorkflowStateHistoryItem))
       
   257 
       
   258 
       
   259 class IWorkflowVersions(Interface):
       
   260     """Interface to get information about versions of content in workflow"""
       
   261 
       
   262     last_version_id = Attribute("Last version ID")
       
   263 
       
   264     def get_version(self, version_id):
       
   265         """Get version matching given id"""
       
   266 
       
   267     def get_versions(self, state=None):
       
   268         """Get all versions of object known for this (optional) state"""
       
   269 
       
   270     def add_version(self, content, state):
       
   271         """Return new unique version id"""
       
   272 
       
   273     def set_state(self, version_id, state):
       
   274         """Set new state for given version"""
       
   275 
       
   276     def has_version(self, state):
       
   277         """Return true if a version exists with the specific workflow state"""
       
   278 
       
   279     def remove_version(self, version_id):
       
   280         """Remove version with given ID"""
       
   281 
       
   282 
       
   283 class IWorkflowManagedContent(IAttributeAnnotatable):
       
   284     """Workflow managed content"""
       
   285 
       
   286     content_class = Attribute("Content class")
       
   287 
       
   288     workflow_name = Choice(title=_("Workflow name"),
       
   289                            description=_("Name of workflow utility managing this content"),
       
   290                            required=True,
       
   291                            vocabulary='PyAMS workflows')
       
   292 
       
   293     view_permission = Choice(title=_("View permission"),
       
   294                              description=_("This permission will be required to display content"),
       
   295                              vocabulary='PyAMS permissions',
       
   296                              required=False)
       
   297 
       
   298 
       
   299 class IWorkflowPublicationSupport(IAttributeAnnotatable):
       
   300     """Workflow publication support"""
       
   301 
       
   302 
       
   303 class IWorkflowVersion(IWorkflowPublicationSupport):
       
   304     """Workflow content version marker interface"""
       
   305 
       
   306 
       
   307 class IWorkflowPublicationInfo(Interface):
       
   308     """Workflow content publication info"""
       
   309 
       
   310     state_date = Datetime(title=_("State date"),
       
   311                           description=_("Date at which the current state was applied"),
       
   312                           readonly=True)
       
   313 
       
   314     state_principal = Principal(title=_("State principal"),
       
   315                                 description=_("ID of the principal which defined current state"))
       
   316 
       
   317     publication_date = Datetime(title=_("Publication date"),
       
   318                                 description=_("Last date at which content was accepted for publication"),
       
   319                                 required=False)
       
   320 
       
   321     first_publication_date = Datetime(title=_("First publication date"),
       
   322                                       description=_("First date at which content was accepted for publication"),
       
   323                                       required=False)
       
   324 
       
   325     publication_effective_date = Datetime(title=_("Publication start date"),
       
   326                                           description=_("Date from which content will be visible"),
       
   327                                           required=False)
       
   328 
       
   329     publication_expiration_date = Datetime(title=_("Publication end date"),
       
   330                                            description=_("Date past which content will not be visible"),
       
   331                                            required=False)
       
   332 
       
   333     @invariant
       
   334     def check_expiration_date(self):
       
   335         if self.publication_expiration_date is not None:
       
   336             if self.publication_effective_date is None:
       
   337                 raise Invalid(_("Can't define publication end date without publication start date!"))
       
   338             if self.publication_effective_date >= self.publication_expiration_date:
       
   339                 raise Invalid(_("Publication end date must be defined after publication start date!"))
       
   340 
       
   341     def reset(self):
       
   342         """Reset all publication info (used by clone features)"""
       
   343 
       
   344     def is_published(self):
       
   345         """Is the content published?"""
       
   346 
       
   347     def is_visible(self, request=None):
       
   348         """Is the content visible?"""
       
   349 
       
   350 
       
   351 class IWorkflowCommentInfo(Interface):
       
   352     """Workflow comment info"""
       
   353 
       
   354     comment = Text(title=_("Comment"),
       
   355                    description=_("Comment associated with this operation"),
       
   356                    required=False)