# HG changeset patch # User Thierry Florac # Date 1505133889 -7200 # Node ID 5b02c3b2eaf2a4227da2a444e859f23802c46fef # Parent 6cdea4a15bfd5ae885023349af6ddf91e8168bda Updated workflow transitions conditions diff -r 6cdea4a15bfd -r 5b02c3b2eaf2 src/pyams_content/workflow/__init__.py --- a/src/pyams_content/workflow/__init__.py Mon Sep 11 14:43:43 2017 +0200 +++ b/src/pyams_content/workflow/__init__.py Mon Sep 11 14:44:49 2017 +0200 @@ -17,13 +17,13 @@ from datetime import datetime # import interfaces -from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, MANAGE_CONTENT_PERMISSION, PUBLISH_CONTENT_PERMISSION, \ - CREATE_CONTENT_PERMISSION -from pyams_content.shared.common.interfaces import IWfSharedContentRoles, IManagerRestrictions, IWfSharedContent +from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, MANAGE_CONTENT_PERMISSION, \ + PUBLISH_CONTENT_PERMISSION, CREATE_CONTENT_PERMISSION +from pyams_content.shared.common.interfaces import IWfSharedContentRoles, IManagerRestrictions from pyams_content.workflow.interfaces import IContentWorkflow from pyams_security.interfaces import IRoleProtectedObject -from pyams_workflow.interfaces import IWorkflow, AUTOMATIC, IWorkflowPublicationInfo, SYSTEM, IWorkflowVersions, \ - IWorkflowState, ObjectClonedEvent, IWorkflowInfo, IWorkflowStateLabel +from pyams_workflow.interfaces import ObjectClonedEvent, IWorkflow, IWorkflowVersions, IWorkflowInfo, \ + IWorkflowState, IWorkflowStateLabel, IWorkflowPublicationInfo, AUTOMATIC, SYSTEM # import packages from pyams_utils.adapter import adapter_config, ContextAdapter @@ -66,20 +66,6 @@ ARCHIVED, DELETED) -UPDATE_STATES = (DRAFT, RETIRED) - -READONLY_STATES = (ARCHIVED, DELETED) - -PROTECTED_STATES = (PUBLISHED, RETIRING, ARCHIVING) - -MANAGER_STATES = (PROPOSED, ) - -VISIBLE_STATES = PUBLISHED_STATES = (PUBLISHED, RETIRING) - -WAITING_STATES = (PROPOSED, RETIRING, ARCHIVING) - -RETIRED_STATES = (RETIRED, ARCHIVING) - STATES_LABELS = (_("Draft"), _("Proposed"), _("Canceled"), @@ -91,6 +77,9 @@ _("Archived"), _("Deleted")) +STATES_VOCABULARY = SimpleVocabulary([SimpleTerm(STATES_IDS[i], title=t) + for i, t in enumerate(STATES_LABELS)]) + STATES_HEADERS = {DRAFT: _("draft created"), PROPOSED: _("publication requested"), PUBLISHED: _("published"), @@ -99,8 +88,24 @@ ARCHIVING: _("archiving requested"), ARCHIVED: _("archived")} -STATES_VOCABULARY = SimpleVocabulary([SimpleTerm(STATES_IDS[i], title=t) - for i, t in enumerate(STATES_LABELS)]) + +UPDATE_STATES = (DRAFT, ) +'''Default state available to contributors in update mode''' + +READONLY_STATES = (RETIRED, ARCHIVING, ARCHIVED, DELETED) +'''Retired and archived contents can't be modified''' + +PROTECTED_STATES = (PUBLISHED, RETIRING) +'''Protected states are available to webmasters in update mode''' + +MANAGER_STATES = (PROPOSED, ) +'''Only managers can update proposed contents (if their restrictions apply)''' + +VISIBLE_STATES = PUBLISHED_STATES = (PUBLISHED, RETIRING) + +WAITING_STATES = (PROPOSED, RETIRING, ARCHIVING) + +RETIRED_STATES = (RETIRED, ARCHIVING) # @@ -109,15 +114,24 @@ def can_propose_content(wf, context): """Check if a content can be proposed""" + # can't propose content if another proposed version already exists versions = IWorkflowVersions(context) if versions.has_version(PROPOSED): return False request = check_request() + # grant access to webmaster if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context): return True - if request.principal.id in context.owner | {context.creator} | context.contributors: + # grant access to owner, creator and local contributors + principal_id = request.principal.id + if principal_id in context.owner | {context.creator} | context.contributors: return True - return False + # grant access to local content managers + if principal_id in context.managers: + return True + # grant access to shared tool managers if restrictions apply + restrictions = IManagerRestrictions(context).get_restrictions(principal_id) + return restrictions and restrictions.check_access(context, permission=MANAGE_CONTENT_PERMISSION, request=request) def can_backdraft_content(wf, context): @@ -132,6 +146,7 @@ def can_create_new_version(wf, context): """Check if we can create a new version""" + # can't create new version when previous draft already exists versions = IWorkflowVersions(context) if (versions.has_version(DRAFT) or versions.has_version(PROPOSED) or @@ -139,40 +154,73 @@ versions.has_version(REFUSED)): return False request = check_request() + # grant access to webmaster if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context): return True - if request.principal.id in context.owner | {context.creator} | context.contributors: + # grant access to owner, creator and local contributors + principal_id = request.principal.id + if principal_id in context.owner | {context.creator} | context.contributors: return True - return False + # grant access to local content managers + if principal_id in context.managers: + return True + # grant access to shared tool managers if restrictions apply + restrictions = IManagerRestrictions(context).get_restrictions(principal_id) + return restrictions and restrictions.check_access(context, permission=CREATE_CONTENT_PERMISSION, request=request) def can_delete_version(wf, context): """Check if we can delete a draft version""" request = check_request() + # grant access to webmaster if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context): return True - return request.principal.id in context.owner | {context.creator} | context.contributors + # grant access to owner, creator and local contributors + principal_id = request.principal.id + if principal_id in context.owner | {context.creator} | context.contributors: + return True + # grant access to local content managers + if principal_id in context.managers: + return True + # grant access to shared tool managers if restrictions apply + restrictions = IManagerRestrictions(context).get_restrictions(principal_id) + return restrictions and restrictions.check_access(context, permission=MANAGE_CONTENT_PERMISSION, request=request) def can_manage_content(wf, context): """Check if a manager can handle content""" request = check_request() + # grant access to webmaster if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context): return True - if request.principal.id in context.managers: + # local content managers can manage content + principal_id = request.principal.id + if principal_id in context.managers: return True - restrictions = IManagerRestrictions(context).get_restrictions(request.principal.id) + # shared tool managers can manage content if restrictions apply + restrictions = IManagerRestrictions(context).get_restrictions(principal_id) return restrictions and restrictions.check_access(context, permission=PUBLISH_CONTENT_PERMISSION, request=request) def can_cancel_operation(wf, context): """Check if we can cancel a request""" request = check_request() + # grant access to webmaster if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context): return True - if request.principal.id in context.owner | {context.creator} | context.contributors: + principal_id = request.principal.id + # workflow actor can cancel it's own request + if principal_id == IWorkflowState(context).state_principal: + return True + # owner, creator and contributors can cancel workflow request + if principal_id in context.owner | {context.creator} | context.contributors: return True - return request.principal.id == IWorkflowState(context).state_principal + # local content managers can cancel workflow request + if principal_id in context.managers: + return True + # shared tool managers can cancel workflow request if restrictions apply + restrictions = IManagerRestrictions(context).get_restrictions(principal_id) + return restrictions and restrictions.check_access(context, permission=MANAGE_CONTENT_PERMISSION, request=request) # @@ -189,7 +237,7 @@ version_id = IWorkflowState(context).version_id for version in IWorkflowVersions(context).get_versions((PUBLISHED, RETIRING, RETIRED, ARCHIVING)): if version is not context: - IWorkflowInfo(version).fire_transition_toward('archived', + IWorkflowInfo(version).fire_transition_toward(ARCHIVED, comment=translate(_("Published version {0}")).format(version_id)) @@ -341,6 +389,13 @@ notify_message=_("A retire request has been submitted for content « {0} »"), order=7) +published_to_retired = Transition(transition_id='published_to_retired', + title=_("Retired content"), + source=PUBLISHED, + destination=RETIRED, + trigger=SYSTEM, + history_label=_("Content retired after passed expiration date")) + retiring_to_published = Transition(transition_id='retiring_to_published', title=_("Cancel retiring request"), source=RETIRING, @@ -515,6 +570,7 @@ refused_to_retired, proposed_to_published, published_to_retiring, + published_to_retired, retiring_to_published, retiring_to_retired, retired_to_archiving, @@ -563,7 +619,7 @@ request = check_request() translate = request.localizer.translate state = IWorkflowState(content) - if len(state.history) == 1: + if len(state.history) <= 2: if state.version_id == 1: state_label = translate(STATES_HEADERS[state.state]) else: @@ -585,7 +641,8 @@ manager_states=MANAGER_STATES, published_states=VISIBLE_STATES, waiting_states=WAITING_STATES, - retired_states=RETIRED_STATES) + retired_states=RETIRED_STATES, + auto_retired_state=RETIRED) @utility_config(name='PyAMS default workflow', provides=IWorkflow)