# HG changeset patch # User Thierry Florac # Date 1547137622 -3600 # Node ID 509f88791c41f543c362abe83e9a53ccb689e841 # Parent 69194c2ab7bc53d018e153ac6b90ec4ae6722b98 Added workflow visible states attribute diff -r 69194c2ab7bc -r 509f88791c41 src/pyams_workflow/content.py --- a/src/pyams_workflow/content.py Wed Jan 09 14:02:56 2019 +0100 +++ b/src/pyams_workflow/content.py Thu Jan 10 17:27:02 2019 +0100 @@ -157,18 +157,18 @@ self.push_end_date = None def is_published(self, check_parent=True): - # check is parent is published + # check is parent is published and visible in front-office if check_parent: parent = get_parent(self.__parent__, IWorkflowPublicationSupport, allow_context=False) if (parent is not None) and not IWorkflowPublicationInfo(parent).is_published(): return False # associated workflow? workflow = IWorkflow(self.__parent__, None) - if (workflow is not None) and not workflow.published_states: + if (workflow is not None) and not workflow.visible_states: return False # check content versions versions = IWorkflowVersions(self.__parent__, None) - if (versions is not None) and not versions.get_versions(workflow.published_states): + if (versions is not None) and not versions.get_versions(workflow.visible_states): return False now = tztime(datetime.utcnow()) return (self.publication_effective_date is not None) and \ diff -r 69194c2ab7bc -r 509f88791c41 src/pyams_workflow/interfaces.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_workflow/interfaces.py Thu Jan 10 17:27:02 2019 +0100 @@ -0,0 +1,502 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + + +# import standard library + +# import interfaces +from pyams_utils.interfaces import VIEW_PERMISSION +from zope.annotation.interfaces import IAttributeAnnotatable +from zope.interface.interfaces import IObjectEvent, ObjectEvent +from zope.lifecycleevent.interfaces import IObjectCreatedEvent + +# import packages +from pyams_security.schema import Principal +from zope.interface import implementer, invariant, Interface, Attribute, Invalid +from zope.lifecycleevent import ObjectCreatedEvent +from zope.schema import Choice, Datetime, Set, TextLine, Text, List, Object, Int, Bool +from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm + +from pyams_workflow import _ + + +MANUAL = 0 +AUTOMATIC = 1 +SYSTEM = 2 + + +class InvalidTransitionError(Exception): + """Base transition error""" + + def __init__(self, source): + self.source = source + + def __str__(self): + return 'source: "%s"' % self.source + + +class NoTransitionAvailableError(InvalidTransitionError): + """Exception raised when there is not available transition""" + + def __init__(self, source, destination): + super(NoTransitionAvailableError, self).__init__(source) + self.destination = destination + + def __str__(self): + return 'source: "%s" destination: "%s"' % (self.source, self.destination) + + +class AmbiguousTransitionError(InvalidTransitionError): + """Exception raised when required transition is ambiguous""" + + def __init__(self, source, destination): + super(AmbiguousTransitionError, self).__init__(source) + self.destination = destination + + def __str__(self): + return 'source: "%s" destination: "%s"' % (self.source, self.destination) + + +class VersionError(Exception): + """Versions management error""" + + +class ConditionFailedError(Exception): + """Exception raised when transition condition failed""" + + +class IWorkflowTransitionEvent(IObjectEvent): + """Workflow transition event interface""" + + wokflow = Attribute("Workflow utility") + + principal = Attribute("Event principal") + + source = Attribute('Original state or None if initial state') + + destination = Attribute('New state') + + transition = Attribute('Transition that was fired or None if initial state') + + comment = Attribute('Comment that went with state transition') + + +@implementer(IWorkflowTransitionEvent) +class WorkflowTransitionEvent(ObjectEvent): + """Workflow transition event""" + + def __init__(self, object, workflow, principal, source, destination, transition, comment): + super(WorkflowTransitionEvent, self).__init__(object) + self.workflow = workflow + self.principal = principal + self.source = source + self.destination = destination + self.transition = transition + self.comment = comment + + +class IWorkflowVersionTransitionEvent(IWorkflowTransitionEvent): + """Workflow version transition event interface""" + + old_object = Attribute('Old version of object') + + +@implementer(IWorkflowVersionTransitionEvent) +class WorkflowVersionTransitionEvent(WorkflowTransitionEvent): + """Workflow version transition event""" + + def __init__(self, object, workflow, principal, old_object, source, destination, transition, comment): + super(WorkflowVersionTransitionEvent, self).__init__(object, workflow, principal, source, + destination, transition, comment) + self.old_object = old_object + + +class IObjectClonedEvent(IObjectCreatedEvent): + """Object cloned event interface""" + + source = Attribute("Cloned object source") + + +@implementer(IObjectClonedEvent) +class ObjectClonedEvent(ObjectCreatedEvent): + """Object cloned event""" + + def __init__(self, object, source): + super(ObjectClonedEvent, self).__init__(object) + self.source = source + + +class IWorkflow(Interface): + """Defines workflow in the form of transition objects. + + Defined as a utility. + """ + + initial_state = Attribute("Initial state") + + update_states = Set(title="Updatable states", + description="States of contents which are updatable by standard contributors") + + readonly_states = Set(title="Read-only states", + description="States of contents which can't be modified by anybody") + + protected_states = Set(title="Protected states", + description="States of contents which can only be modified by site administrators") + + manager_states = Set(title="Manager states", + description="States of contents which can be modified by site administrators and content " + "managers") + + published_states = Set(title="Published states", + description="States of contents which are published") + + visible_states = Set(title="Visible staets", + description="States of contents which are visible in front-office") + + waiting_states = Set(title="Waiting states", + description="States of contents waiting for action") + + retired_states = Set(title="Retired states", + description="States of contents which are retired but not yet archived") + + auto_retired_state = Attribute("Auto-retired state") + + def initialize(self): + """Do any needed initialization. + + Such as initialization with the workflow versions system. + """ + + def refresh(self, transitions): + """Refresh workflow completely with new transitions.""" + + def get_state_label(self, state): + """Get given state label""" + + def get_transitions(self, source): + """Get all transitions from source""" + + def get_transition(self, source, transition_id): + """Get transition with transition_id given source state. + + If the transition is invalid from this source state, + an InvalidTransitionError is raised. + """ + + def get_transition_by_id(self, transition_id): + """Get transition with transition_id""" + + +class IWorkflowInfo(Interface): + """Get workflow info about workflowed object, and drive workflow. + + Defined as an adapter. + """ + + def set_initial_state(self, state, comment=None): + """Set initial state for the context object. + + Fires a transition event. + """ + + def fire_transition(self, transition_id, comment=None, side_effect=None, check_security=True, principal=None): + """Fire a transition for the context object. + + There's an optional comment parameter that contains some + opaque object that offers a comment about the transition. + This is useful for manual transitions where users can motivate + their actions. + + There's also an optional side effect parameter which should + be a callable which receives the object undergoing the transition + as the parameter. This could do an editing action of the newly + transitioned workflow object before an actual transition event is + fired. + + If check_security is set to False, security is not checked + and an application can fire a transition no matter what the + user's permission is. + """ + + def fire_transition_toward(self, state, comment=None, side_effect=None, check_security=True, principal=None): + """Fire transition toward state. + + Looks up a manual transition that will get to the indicated + state. + + If no such transition is possible, NoTransitionAvailableError will + be raised. + + If more than one manual transitions are possible, + AmbiguousTransitionError will be raised. + """ + + def fire_transition_for_versions(self, state, transition_id, comment=None, principal=None): + """Fire a transition for all versions in a state""" + + def fire_automatic(self): + """Fire automatic transitions if possible by condition""" + + def has_version(self, state): + """Return true if a version exists in given state""" + + def get_manual_transition_ids(self): + """Returns list of valid manual transitions. + + These transitions have to have a condition that's True. + """ + + def get_manual_transition_ids_toward(self, state): + """Returns list of manual transitions towards state""" + + def get_automatic_transition_ids(self): + """Returns list of possible automatic transitions. + + Condition is not checked. + """ + + def has_automatic_transitions(self): + """Return true if there are possible automatic outgoing transitions. + + Condition is not checked. + """ + + +class IWorkflowStateHistoryItem(Interface): + """Workflow state history item""" + + date = Datetime(title="State change datetime", + required=True) + + source_version = Int(title="Source version ID", + required=False) + + source_state = TextLine(title="Transition source state", + required=False) + + target_state = TextLine(title="Transition target state", + required=True) + + transition_id = TextLine(title="Transition ID", + required=True) + + transition = TextLine(title="Transition name", + required=True) + + principal = Principal(title="Transition principal", + required=False) + + comment = Text(title="Transition comment", + required=False) + + +class IWorkflowState(Interface): + """Store state on workflowed objects. + + Defined as an adapter. + """ + + version_id = Int(title=_("Version ID")) + + state = TextLine(title=_("Version state")) + + state_date = Datetime(title=_("State date"), + description=_("Date at which the current state was applied")) + + state_principal = Principal(title=_("State principal"), + description=_("ID of the principal which defined current state")) + + state_urgency = Bool(title=_("Urgent request?"), + required=True, + default=False) + + history = List(title="Workflow states history", + value_type=Object(schema=IWorkflowStateHistoryItem)) + + def get_first_state_date(self, states): + """Get first date at which given state was set""" + + +class IWorkflowVersions(Interface): + """Interface to get information about versions of content in workflow""" + + last_version_id = Attribute("Last version ID") + + def get_version(self, version_id): + """Get version matching given id""" + + def get_versions(self, states=None, sort=False, reverse=False): + """Get all versions of object known for this (optional) state""" + + def get_last_versions(self, count=1): + """Get last versions of this object. Set count=0 to get all versions.""" + + def add_version(self, content, state, principal=None): + """Return new unique version id""" + + def set_state(self, version_id, state, principal=None): + """Set new state for given version""" + + def has_version(self, state): + """Return true if a version exists with the specific workflow state""" + + def remove_version(self, version_id, state='deleted', comment=None, principal=None): + """Remove version with given ID""" + + +class IWorkflowStateLabel(Interface): + """Workflow state label adapter interface""" + + def get_label(self, content, request=None, format=True): + """Get state label for given content""" + + +class IWorkflowManagedContent(IAttributeAnnotatable): + """Workflow managed content""" + + content_class = Attribute("Content class") + + workflow_name = Choice(title=_("Workflow name"), + description=_("Name of workflow utility managing this content"), + required=True, + vocabulary='PyAMS workflows') + + view_permission = Choice(title=_("View permission"), + description=_("This permission will be required to display content"), + vocabulary='PyAMS permissions', + default=VIEW_PERMISSION, + required=False) + + +class IWorkflowPublicationSupport(IAttributeAnnotatable): + """Workflow publication support""" + + +class IWorkflowVersion(IWorkflowPublicationSupport): + """Workflow content version marker interface""" + + +class IWorkflowTransitionInfo(Interface): + """Workflow transition info""" + + transition_id = TextLine(title=_("Transition ID"), + required=True) + + +DISPLAY_FIRST_VERSION = 'first' +DISPLAY_CURRENT_VERSION = 'current' + +VERSION_DISPLAY = {DISPLAY_FIRST_VERSION: _("Display first version date"), + DISPLAY_CURRENT_VERSION: _("Display current version date")} + +VERSION_DISPLAY_VOCABULARY = SimpleVocabulary([SimpleTerm(v, title=t) + for v, t in VERSION_DISPLAY.items()]) + + +class IWorkflowPublicationInfo(Interface): + """Workflow content publication info""" + + publication_date = Datetime(title=_("Publication date"), + description=_("Last date at which content was accepted for publication"), + required=False) + + publisher = Principal(title=_("Publisher"), + description=_("Name of the manager who published the document"), + required=False) + + publication = TextLine(title=_("Publication"), + description=_("Last publication date and actor"), + required=False, + readonly=True) + + first_publication_date = Datetime(title=_("First publication date"), + description=_("First date at which content was accepted for publication"), + required=False) + + publication_effective_date = Datetime(title=_("Publication start date"), + description=_("Date from which content will be visible"), + required=False) + + push_end_date = Datetime(title=_("Push end date"), + description=_("Some contents can be pushed by components to front-office pages; if you " + "set a date here, this content will not be pushed anymore passed this " + "date, but will still be available via search engine or direct links"), + required=False) + + push_end_date_index = Attribute("Push end date value used by catalog indexes") + + @invariant + def check_push_end_date(self): + if self.push_end_date is not None: + if self.publication_effective_date is None: + raise Invalid(_("Can't define push end date without publication start date!")) + if self.publication_effective_date >= self.push_end_date: + raise Invalid(_("Push end date must be defined after publication start date!")) + if self.publication_expiration_date is not None: + if self.publication_expiration_date < self.push_end_date: + raise Invalid(_("Push end date must be null or defined before publication end date!")) + + publication_expiration_date = Datetime(title=_("Publication end date"), + description=_("Date past which content will not be visible"), + required=False) + + @invariant + def check_expiration_date(self): + if self.publication_expiration_date is not None: + if self.publication_effective_date is None: + raise Invalid(_("Can't define publication end date without publication start date!")) + if self.publication_effective_date >= self.publication_expiration_date: + raise Invalid(_("Publication end date must be defined after publication start date!")) + + displayed_publication_date = Choice(title=_("Displayed publication date"), + description=_("The matching date will be displayed in front-office"), + vocabulary='PyAMS content publication date', + default=DISPLAY_FIRST_VERSION, + required=True) + + visible_publication_date = Attribute("Visible publication date") + + def reset(self, complete=True): + """Reset all publication info (used by clone features) + + If 'complete' argument is True, all date fields are reset; otherwise, push and publication end dates are + preserved in new versions. + """ + + def is_published(self, check_parent=True): + """Is the content published?""" + + def is_visible(self, request=None, check_parent=True): + """Is the content visible?""" + + +class IWorkflowRequestUrgencyInfo(Interface): + """Workflow request urgency info""" + + urgent_request = Bool(title=_("Urgent request?"), + description=_("Please use this option only when really needed..."), + required=True, + default=False) + + +class IWorkflowCommentInfo(Interface): + """Workflow comment info""" + + comment = Text(title=_("Comment"), + description=_("Comment associated with this operation"), + required=False) + + +class IWorkflowManagementTask(Interface): + """Workflow management task marker interface""" diff -r 69194c2ab7bc -r 509f88791c41 src/pyams_workflow/interfaces/__init__.py --- a/src/pyams_workflow/interfaces/__init__.py Wed Jan 09 14:02:56 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,495 +0,0 @@ -# -# Copyright (c) 2008-2015 Thierry Florac -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# - -__docformat__ = 'restructuredtext' - - -# import standard library - -# import interfaces -from pyams_utils.interfaces import VIEW_PERMISSION -from zope.annotation.interfaces import IAttributeAnnotatable -from zope.interface.interfaces import IObjectEvent, ObjectEvent -from zope.lifecycleevent.interfaces import IObjectCreatedEvent - -# import packages -from pyams_security.schema import Principal -from zope.interface import implementer, invariant, Interface, Attribute, Invalid -from zope.lifecycleevent import ObjectCreatedEvent -from zope.schema import Choice, Datetime, Set, TextLine, Text, List, Object, Int, Bool -from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm - -from pyams_workflow import _ - - -MANUAL = 0 -AUTOMATIC = 1 -SYSTEM = 2 - - -class InvalidTransitionError(Exception): - """Base transition error""" - - def __init__(self, source): - self.source = source - - def __str__(self): - return 'source: "%s"' % self.source - - -class NoTransitionAvailableError(InvalidTransitionError): - """Exception raised when there is not available transition""" - - def __init__(self, source, destination): - super(NoTransitionAvailableError, self).__init__(source) - self.destination = destination - - def __str__(self): - return 'source: "%s" destination: "%s"' % (self.source, self.destination) - - -class AmbiguousTransitionError(InvalidTransitionError): - """Exception raised when required transition is ambiguous""" - - def __init__(self, source, destination): - super(AmbiguousTransitionError, self).__init__(source) - self.destination = destination - - def __str__(self): - return 'source: "%s" destination: "%s"' % (self.source, self.destination) - - -class VersionError(Exception): - """Versions management error""" - - -class ConditionFailedError(Exception): - """Exception raised when transition condition failed""" - - -class IWorkflowTransitionEvent(IObjectEvent): - """Workflow transition event interface""" - - wokflow = Attribute("Workflow utility") - - principal = Attribute("Event principal") - - source = Attribute('Original state or None if initial state') - - destination = Attribute('New state') - - transition = Attribute('Transition that was fired or None if initial state') - - comment = Attribute('Comment that went with state transition') - - -@implementer(IWorkflowTransitionEvent) -class WorkflowTransitionEvent(ObjectEvent): - """Workflow transition event""" - - def __init__(self, object, workflow, principal, source, destination, transition, comment): - super(WorkflowTransitionEvent, self).__init__(object) - self.workflow = workflow - self.principal = principal - self.source = source - self.destination = destination - self.transition = transition - self.comment = comment - - -class IWorkflowVersionTransitionEvent(IWorkflowTransitionEvent): - """Workflow version transition event interface""" - - old_object = Attribute('Old version of object') - - -@implementer(IWorkflowVersionTransitionEvent) -class WorkflowVersionTransitionEvent(WorkflowTransitionEvent): - """Workflow version transition event""" - - def __init__(self, object, workflow, principal, old_object, source, destination, transition, comment): - super(WorkflowVersionTransitionEvent, self).__init__(object, workflow, principal, source, - destination, transition, comment) - self.old_object = old_object - - -class IObjectClonedEvent(IObjectCreatedEvent): - """Object cloned event interface""" - - source = Attribute("Cloned object source") - - -@implementer(IObjectClonedEvent) -class ObjectClonedEvent(ObjectCreatedEvent): - """Object cloned event""" - - def __init__(self, object, source): - super(ObjectClonedEvent, self).__init__(object) - self.source = source - - -class IWorkflow(Interface): - """Defines workflow in the form of transition objects. - - Defined as a utility. - """ - - initial_state = Attribute("Initial state") - - update_states = Set(title="Updatable states", - description="States of contents which are updatable by standard contributors") - - readonly_states = Set(title="Read-only states", - description="States of contents which can't be modified by anybody") - - protected_states = Set(title="Protected states", - description="States of contents which can only be modified by site administrators") - - manager_states = Set(title="Manager states", - description="States of contents which can be modified by site administrators and content " - "managers") - - published_states = Set(title="Published states", - description="States of contents which are published and visible") - - waiting_states = Set(title="Waiting states", - description="States of contents waiting for action") - - retired_states = Set(title="Retired states", - description="States of contents which are retired but not yet archived") - - auto_retired_state = Attribute("Auto-retired state") - - def initialize(self): - """Do any needed initialization. - - Such as initialization with the workflow versions system. - """ - - def refresh(self, transitions): - """Refresh workflow completely with new transitions.""" - - def get_state_label(self, state): - """Get given state label""" - - def get_transitions(self, source): - """Get all transitions from source""" - - def get_transition(self, source, transition_id): - """Get transition with transition_id given source state. - - If the transition is invalid from this source state, - an InvalidTransitionError is raised. - """ - - def get_transition_by_id(self, transition_id): - """Get transition with transition_id""" - - -class IWorkflowInfo(Interface): - """Get workflow info about workflowed object, and drive workflow. - - Defined as an adapter. - """ - - def set_initial_state(self, state, comment=None): - """Set initial state for the context object. - - Fires a transition event. - """ - - def fire_transition(self, transition_id, comment=None, side_effect=None, check_security=True, principal=None): - """Fire a transition for the context object. - - There's an optional comment parameter that contains some - opaque object that offers a comment about the transition. - This is useful for manual transitions where users can motivate - their actions. - - There's also an optional side effect parameter which should - be a callable which receives the object undergoing the transition - as the parameter. This could do an editing action of the newly - transitioned workflow object before an actual transition event is - fired. - - If check_security is set to False, security is not checked - and an application can fire a transition no matter what the - user's permission is. - """ - - def fire_transition_toward(self, state, comment=None, side_effect=None, check_security=True, principal=None): - """Fire transition toward state. - - Looks up a manual transition that will get to the indicated - state. - - If no such transition is possible, NoTransitionAvailableError will - be raised. - - If more than one manual transitions are possible, - AmbiguousTransitionError will be raised. - """ - - def fire_transition_for_versions(self, state, transition_id, comment=None, principal=None): - """Fire a transition for all versions in a state""" - - def fire_automatic(self): - """Fire automatic transitions if possible by condition""" - - def has_version(self, state): - """Return true if a version exists in given state""" - - def get_manual_transition_ids(self): - """Returns list of valid manual transitions. - - These transitions have to have a condition that's True. - """ - - def get_manual_transition_ids_toward(self, state): - """Returns list of manual transitions towards state""" - - def get_automatic_transition_ids(self): - """Returns list of possible automatic transitions. - - Condition is not checked. - """ - - def has_automatic_transitions(self): - """Return true if there are possible automatic outgoing transitions. - - Condition is not checked. - """ - - -class IWorkflowStateHistoryItem(Interface): - """Workflow state history item""" - - date = Datetime(title="State change datetime", - required=True) - - source_version = Int(title="Source version ID", - required=False) - - source_state = TextLine(title="Transition source state", - required=False) - - target_state = TextLine(title="Transition target state", - required=True) - - transition_id = TextLine(title="Transition ID", - required=True) - - transition = TextLine(title="Transition name", - required=True) - - principal = Principal(title="Transition principal", - required=False) - - comment = Text(title="Transition comment", - required=False) - - -class IWorkflowState(Interface): - """Store state on workflowed objects. - - Defined as an adapter. - """ - - version_id = Int(title=_("Version ID")) - - state = TextLine(title=_("Version state")) - - state_date = Datetime(title=_("State date"), - description=_("Date at which the current state was applied")) - - state_principal = Principal(title=_("State principal"), - description=_("ID of the principal which defined current state")) - - state_urgency = Bool(title=_("Urgent request?"), - required=True, - default=False) - - history = List(title="Workflow states history", - value_type=Object(schema=IWorkflowStateHistoryItem)) - - def get_first_state_date(self, states): - """Get first date at which given state was set""" - - -class IWorkflowVersions(Interface): - """Interface to get information about versions of content in workflow""" - - last_version_id = Attribute("Last version ID") - - def get_version(self, version_id): - """Get version matching given id""" - - def get_versions(self, states=None, sort=False, reverse=False): - """Get all versions of object known for this (optional) state""" - - def get_last_versions(self, count=1): - """Get last versions of this object. Set count=0 to get all versions.""" - - def add_version(self, content, state, principal=None): - """Return new unique version id""" - - def set_state(self, version_id, state, principal=None): - """Set new state for given version""" - - def has_version(self, state): - """Return true if a version exists with the specific workflow state""" - - def remove_version(self, version_id, state='deleted', comment=None, principal=None): - """Remove version with given ID""" - - -class IWorkflowStateLabel(Interface): - """Workflow state label adapter interface""" - - def get_label(self, content, request=None, format=True): - """Get state label for given content""" - - -class IWorkflowManagedContent(IAttributeAnnotatable): - """Workflow managed content""" - - content_class = Attribute("Content class") - - workflow_name = Choice(title=_("Workflow name"), - description=_("Name of workflow utility managing this content"), - required=True, - vocabulary='PyAMS workflows') - - view_permission = Choice(title=_("View permission"), - description=_("This permission will be required to display content"), - vocabulary='PyAMS permissions', - default=VIEW_PERMISSION, - required=False) - - -class IWorkflowPublicationSupport(IAttributeAnnotatable): - """Workflow publication support""" - - -class IWorkflowVersion(IWorkflowPublicationSupport): - """Workflow content version marker interface""" - - -class IWorkflowTransitionInfo(Interface): - """Workflow transition info""" - - transition_id = TextLine(title=_("Transition ID"), - required=True) - - -DISPLAY_FIRST_VERSION = 'first' -DISPLAY_CURRENT_VERSION = 'current' - -VERSION_DISPLAY = {DISPLAY_FIRST_VERSION: _("Display first version date"), - DISPLAY_CURRENT_VERSION: _("Display current version date")} - -VERSION_DISPLAY_VOCABULARY = SimpleVocabulary([SimpleTerm(v, title=t) - for v, t in VERSION_DISPLAY.items()]) - - -class IWorkflowPublicationInfo(Interface): - """Workflow content publication info""" - - publication_date = Datetime(title=_("Publication date"), - description=_("Last date at which content was accepted for publication"), - required=False) - - publisher = Principal(title=_("Publisher"), - description=_("Name of the manager who published the document"), - required=False) - - publication = TextLine(title=_("Publication"), - description=_("Last publication date and actor"), - required=False, - readonly=True) - - first_publication_date = Datetime(title=_("First publication date"), - description=_("First date at which content was accepted for publication"), - required=False) - - publication_effective_date = Datetime(title=_("Publication start date"), - description=_("Date from which content will be visible"), - required=False) - - push_end_date = Datetime(title=_("Push end date"), - description=_("Some contents can be pushed by components to front-office pages; if you " - "set a date here, this content will not be pushed anymore passed this " - "date, but will still be available via search engine or direct links"), - required=False) - - push_end_date_index = Attribute("Push end date value used by catalog indexes") - - @invariant - def check_push_end_date(self): - if self.push_end_date is not None: - if self.publication_effective_date is None: - raise Invalid(_("Can't define push end date without publication start date!")) - if self.publication_effective_date >= self.push_end_date: - raise Invalid(_("Push end date must be defined after publication start date!")) - if self.publication_expiration_date is not None: - if self.publication_expiration_date < self.push_end_date: - raise Invalid(_("Push end date must be null or defined before publication end date!")) - - publication_expiration_date = Datetime(title=_("Publication end date"), - description=_("Date past which content will not be visible"), - required=False) - - @invariant - def check_expiration_date(self): - if self.publication_expiration_date is not None: - if self.publication_effective_date is None: - raise Invalid(_("Can't define publication end date without publication start date!")) - if self.publication_effective_date >= self.publication_expiration_date: - raise Invalid(_("Publication end date must be defined after publication start date!")) - - displayed_publication_date = Choice(title=_("Displayed publication date"), - description=_("The matching date will be displayed in front-office"), - vocabulary='PyAMS content publication date', - default=DISPLAY_FIRST_VERSION, - required=True) - - visible_publication_date = Attribute("Visible publication date") - - def reset(self, complete=True): - """Reset all publication info (used by clone features) - - If 'complete' argument is True, all date fields are reset; otherwise, push and publication end dates are - preserved in new versions. - """ - - def is_published(self, check_parent=True): - """Is the content published?""" - - def is_visible(self, request=None, check_parent=True): - """Is the content visible?""" - - -class IWorkflowRequestUrgencyInfo(Interface): - """Workflow request urgency info""" - - urgent_request = Bool(title=_("Urgent request?"), - description=_("Please use this option only when really needed..."), - required=True, - default=False) - - -class IWorkflowCommentInfo(Interface): - """Workflow comment info""" - - comment = Text(title=_("Comment"), - description=_("Comment associated with this operation"), - required=False) diff -r 69194c2ab7bc -r 509f88791c41 src/pyams_workflow/workflow.py --- a/src/pyams_workflow/workflow.py Wed Jan 09 14:02:56 2019 +0100 +++ b/src/pyams_workflow/workflow.py Thu Jan 10 17:27:02 2019 +0100 @@ -85,6 +85,7 @@ protected_states=None, manager_states=None, published_states=None, + visible_states=None, waiting_states=None, retired_states=None, archived_states=None, @@ -92,14 +93,15 @@ self.refresh(transitions) self.states = states self.initial_state = initial_state - self.update_states = set(update_states) if update_states else set() - self.readonly_states = set(readonly_states) if readonly_states else set() - self.protected_states = set(protected_states) if protected_states else set() - self.manager_states = set(manager_states) if manager_states else set() - self.published_states = set(published_states) if published_states else set() - self.waiting_states = set(waiting_states) if waiting_states else set() - self.retired_states = set(retired_states) if retired_states else set() - self.archived_states = set(archived_states) if archived_states else set() + self.update_states = set(update_states or ()) + self.readonly_states = set(readonly_states or ()) + self.protected_states = set(protected_states or ()) + self.manager_states = set(manager_states or ()) + self.published_states = set(published_states or ()) + self.visible_states = set(visible_states) if visible_states else self.published_states + self.waiting_states = set(waiting_states or ()) + self.retired_states = set(retired_states or ()) + self.archived_states = set(archived_states or ()) self.auto_retired_state = auto_retired_state def _register(self, transition):