src/pyams_content/workflow/__init__.py
changeset 0 7c0001cacf8e
child 10 cd69b40debd7
equal deleted inserted replaced
-1:000000000000 0:7c0001cacf8e
       
     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 from datetime import datetime
       
    18 
       
    19 # import interfaces
       
    20 from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, MANAGE_CONTENT_PERMISSION, PUBLISH_CONTENT_PERMISSION, \
       
    21     CREATE_CONTENT_PERMISSION
       
    22 from pyams_content.shared.common.interfaces import IWfSharedContentRoles, IManagerRestrictions
       
    23 from pyams_content.workflow.interfaces import IContentWorkflow
       
    24 from pyams_security.interfaces import IRoleProtectedObject, ISecurityManager
       
    25 from pyams_workflow.interfaces import IWorkflow, AUTOMATIC, IWorkflowPublicationInfo, SYSTEM, IWorkflowVersions, \
       
    26     IWorkflowState, ObjectClonedEvent, IWorkflowInfo, IWorkflowStateLabel
       
    27 
       
    28 # import packages
       
    29 from pyams_utils.adapter import adapter_config, ContextAdapter
       
    30 from pyams_utils.registry import utility_config, get_utility
       
    31 from pyams_utils.request import check_request
       
    32 from pyams_workflow.workflow import Transition, Workflow
       
    33 from pyramid.threadlocal import get_current_registry
       
    34 from zope.copy import copy
       
    35 from zope.interface import implementer
       
    36 from zope.location import locate
       
    37 from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
       
    38 
       
    39 from pyams_content import _
       
    40 
       
    41 
       
    42 #
       
    43 # Workflow states
       
    44 #
       
    45 
       
    46 DRAFT = 'draft'
       
    47 PROPOSED = 'proposed'
       
    48 CANCELED = 'canceled'
       
    49 REFUSED = 'refused'
       
    50 PUBLISHED = 'published'
       
    51 RETIRING = 'retiring'
       
    52 RETIRED = 'retired'
       
    53 ARCHIVING = 'archiving'
       
    54 ARCHIVED = 'archived'
       
    55 DELETED = 'deleted'
       
    56 
       
    57 STATES_IDS = (DRAFT,
       
    58               PROPOSED,
       
    59               CANCELED,
       
    60               REFUSED,
       
    61               PUBLISHED,
       
    62               RETIRING,
       
    63               RETIRED,
       
    64               ARCHIVING,
       
    65               ARCHIVED,
       
    66               DELETED)
       
    67 
       
    68 UPDATE_STATES = (DRAFT, RETIRED)
       
    69 
       
    70 READONLY_STATES = (ARCHIVED, DELETED)
       
    71 
       
    72 PROTECTED_STATES = (PUBLISHED, RETIRING, ARCHIVING)
       
    73 
       
    74 MANAGER_STATES = (PROPOSED, )
       
    75 
       
    76 VISIBLE_STATES = PUBLISHED_STATES = (PUBLISHED, RETIRING)
       
    77 
       
    78 WAITING_STATES = (PROPOSED, RETIRING, ARCHIVING)
       
    79 
       
    80 RETIRED_STATES = (RETIRED, ARCHIVING)
       
    81 
       
    82 STATES_LABELS = (_("Draft"),
       
    83                  _("Proposed"),
       
    84                  _("Canceled"),
       
    85                  _("Refused"),
       
    86                  _("Published"),
       
    87                  _("Retiring"),
       
    88                  _("Retired"),
       
    89                  _("Archiving"),
       
    90                  _("Archived"),
       
    91                  _("Deleted"))
       
    92 
       
    93 STATES_HEADERS = {DRAFT: _("draft created by {principal}"),
       
    94                   PROPOSED: _("publication requested by {principal}"),
       
    95                   PUBLISHED: _("published by {principal}"),
       
    96                   RETIRING: _("retiring requested by {principal}"),
       
    97                   RETIRED: _("retired by {principal}"),
       
    98                   ARCHIVING: _("archiving requested by {principal}"),
       
    99                   ARCHIVED: _("archived by {principal}")}
       
   100 
       
   101 STATES_VOCABULARY = SimpleVocabulary([SimpleTerm(STATES_IDS[i], title=t)
       
   102                                       for i, t in enumerate(STATES_LABELS)])
       
   103 
       
   104 
       
   105 #
       
   106 # Workflow conditions
       
   107 #
       
   108 
       
   109 def can_propose_content(wf, context):
       
   110     """Check if a content can be proposed"""
       
   111     versions = IWorkflowVersions(context)
       
   112     if versions.has_version(PROPOSED):
       
   113         return False
       
   114     request = check_request()
       
   115     if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context):
       
   116         return True
       
   117     if request.principal.id in context.owner | {context.creator} | context.contributors:
       
   118         return True
       
   119     return False
       
   120 
       
   121 
       
   122 def can_backdraft_content(wf, context):
       
   123     """Check if content can return to DRAFT state"""
       
   124     return IWorkflowPublicationInfo(context).publication_date is None
       
   125 
       
   126 
       
   127 def can_retire_content(wf, context):
       
   128     """Check if already published content can return to RETIRED state"""
       
   129     return IWorkflowPublicationInfo(context).publication_date is not None
       
   130 
       
   131 
       
   132 def can_create_new_version(wf, context):
       
   133     """Check if we can create a new version"""
       
   134     versions = IWorkflowVersions(context)
       
   135     if (versions.has_version(DRAFT) or
       
   136         versions.has_version(PROPOSED) or
       
   137         versions.has_version(CANCELED) or
       
   138         versions.has_version(REFUSED)):
       
   139         return False
       
   140     request = check_request()
       
   141     if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context):
       
   142         return True
       
   143     if request.principal.id in context.owner | {context.creator} | context.contributors:
       
   144         return True
       
   145     return False
       
   146 
       
   147 
       
   148 def can_delete_version(wf, context):
       
   149     """Check if we can delete a draft version"""
       
   150     request = check_request()
       
   151     if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context):
       
   152         return True
       
   153     return request.principal.id in context.owner | {context.creator} | context.contributors
       
   154 
       
   155 
       
   156 def can_manage_content(wf, context):
       
   157     """Check if a manager can handle content"""
       
   158     request = check_request()
       
   159     if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context):
       
   160         return True
       
   161     if request.principal.id in context.managers:
       
   162         return True
       
   163     restrictions = IManagerRestrictions(context).get_restrictions(request.principal.id)
       
   164     return restrictions and restrictions.check_access(context, permission=PUBLISH_CONTENT_PERMISSION, request=request)
       
   165 
       
   166 
       
   167 def can_cancel_operation(wf, context):
       
   168     """Check if we can cancel a request"""
       
   169     request = check_request()
       
   170     if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, context):
       
   171         return True
       
   172     if request.principal.id in context.owner | {context.creator} | context.contributors:
       
   173         return True
       
   174     return request.principal.id == IWorkflowState(context).state_principal
       
   175 
       
   176 
       
   177 #
       
   178 # Workflow actions
       
   179 #
       
   180 
       
   181 def publish_action(wf, context):
       
   182     """Publish version"""
       
   183     IWorkflowPublicationInfo(context).publication_date = datetime.utcnow()
       
   184     translate = check_request().localizer.translate
       
   185     version_id = IWorkflowState(context).version_id
       
   186     for version in IWorkflowVersions(context).get_versions((PUBLISHED, RETIRING, RETIRED, ARCHIVING)):
       
   187         if version is not context:
       
   188             IWorkflowInfo(version).fire_transition_toward('archived',
       
   189                                                           comment=translate(_("Published version {0}")).format(version_id))
       
   190 
       
   191 
       
   192 def archive_action(wf, context):
       
   193     """Remove readers when a content is archived"""
       
   194     roles = IWfSharedContentRoles(context, None)
       
   195     if roles is not None:
       
   196         IRoleProtectedObject(context).revoke_role('pyams.Reader', roles.readers)
       
   197 
       
   198 
       
   199 def clone_action(wf, context):
       
   200     """Create new version"""
       
   201     result = copy(context)
       
   202     locate(result, context.__parent__)
       
   203     registry = get_current_registry()
       
   204     registry.notify(ObjectClonedEvent(result, context))
       
   205     return result
       
   206 
       
   207 
       
   208 def delete_action(wf, context):
       
   209     """Delete draft version, and parent if single version"""
       
   210     versions = IWorkflowVersions(context)
       
   211     versions.remove_version(IWorkflowState(context).version_id)
       
   212 
       
   213 
       
   214 #
       
   215 # Workflow transitions
       
   216 #
       
   217 
       
   218 init = Transition(transition_id='init',
       
   219                   title=_("Initialize"),
       
   220                   source=None,
       
   221                   destination=DRAFT,
       
   222                   history_label=_("Draft creation"))
       
   223 
       
   224 draft_to_proposed = Transition(transition_id='draft_to_proposed',
       
   225                                title=_("Propose publication"),
       
   226                                source=DRAFT,
       
   227                                destination=PROPOSED,
       
   228                                permission=MANAGE_CONTENT_PERMISSION,
       
   229                                condition=can_propose_content,
       
   230                                menu_css_class='fa fa-fw fa-question',
       
   231                                view_name='wf-propose.html',
       
   232                                history_label=_("Publication request"),
       
   233                                next_step=_("content managers authorized to take charge of your content are going to "
       
   234                                            "be notified of your request."),
       
   235                                order=1)
       
   236 
       
   237 retired_to_proposed = Transition(transition_id='retired_to_proposed',
       
   238                                  title=_("Propose publication"),
       
   239                                  source=RETIRED,
       
   240                                  destination=PROPOSED,
       
   241                                  permission=MANAGE_CONTENT_PERMISSION,
       
   242                                  condition=can_propose_content,
       
   243                                  menu_css_class='fa fa-fw fa-question',
       
   244                                  view_name='wf-propose.html',
       
   245                                  history_label=_("Publication request"),
       
   246                                  next_step=_("content managers authorized to take charge of your content are going to "
       
   247                                              "be notified of your request."),
       
   248                                  order=1)
       
   249 
       
   250 proposed_to_canceled = Transition(transition_id='proposed_to_canceled',
       
   251                                   title=_("Cancel publication request"),
       
   252                                   source=PROPOSED,
       
   253                                   destination=CANCELED,
       
   254                                   permission=MANAGE_CONTENT_PERMISSION,
       
   255                                   condition=can_cancel_operation,
       
   256                                   menu_css_class='fa fa-fw fa-mail-reply',
       
   257                                   view_name='wf-cancel-propose.html',
       
   258                                   history_label=_("Publication request canceled"),
       
   259                                   order=2)
       
   260 
       
   261 canceled_to_draft = Transition(transition_id='canceled_to_draft',
       
   262                                title=_("Reset canceled publication to draft"),
       
   263                                source=CANCELED,
       
   264                                destination=DRAFT,
       
   265                                trigger=AUTOMATIC,
       
   266                                history_label=_("State reset to 'draft' (automatic)"),
       
   267                                condition=can_backdraft_content)
       
   268 
       
   269 canceled_to_retired = Transition(transition_id='canceled_to_retired',
       
   270                                  title=_("Reset canceled publication to retired"),
       
   271                                  source=CANCELED,
       
   272                                  destination=RETIRED,
       
   273                                  trigger=AUTOMATIC,
       
   274                                  history_label=_("State reset to 'retired' (automatic)"),
       
   275                                  condition=can_retire_content)
       
   276 
       
   277 proposed_to_refused = Transition(transition_id='proposed_to_refused',
       
   278                                  title=_("Refuse publication"),
       
   279                                  source=PROPOSED,
       
   280                                  destination=REFUSED,
       
   281                                  permission=PUBLISH_CONTENT_PERMISSION,
       
   282                                  condition=can_manage_content,
       
   283                                  menu_css_class='fa fa-fw fa-thumbs-o-down',
       
   284                                  view_name='wf-refuse.html',
       
   285                                  history_label=_("Publication refused"),
       
   286                                  order=3)
       
   287 
       
   288 refused_to_draft = Transition(transition_id='refused_to_draft',
       
   289                               title=_("Reset refused publication to draft"),
       
   290                               source=REFUSED,
       
   291                               destination=DRAFT,
       
   292                               trigger=AUTOMATIC,
       
   293                               history_label=_("State reset to 'draft' (automatic)"),
       
   294                               condition=can_backdraft_content)
       
   295 
       
   296 refused_to_retired = Transition(transition_id='refused_to_retired',
       
   297                                 title=_("Reset refused publication to retired"),
       
   298                                 source=REFUSED,
       
   299                                 destination=RETIRED,
       
   300                                 trigger=AUTOMATIC,
       
   301                                 history_label=_("State reset to 'refused' (automatic)"),
       
   302                                 condition=can_retire_content)
       
   303 
       
   304 proposed_to_published = Transition(transition_id='proposed_to_published',
       
   305                                    title=_("Publish content"),
       
   306                                    source=PROPOSED,
       
   307                                    destination=PUBLISHED,
       
   308                                    permission=PUBLISH_CONTENT_PERMISSION,
       
   309                                    condition=can_manage_content,
       
   310                                    action=publish_action,
       
   311                                    menu_css_class='fa fa-fw fa-thumbs-o-up',
       
   312                                    view_name='wf-publish.html',
       
   313                                    history_label=_("Content published"),
       
   314                                    order=4)
       
   315 
       
   316 published_to_retiring = Transition(transition_id='published_to_retiring',
       
   317                                    title=_("Request retiring"),
       
   318                                    source=PUBLISHED,
       
   319                                    destination=RETIRING,
       
   320                                    permission=MANAGE_CONTENT_PERMISSION,
       
   321                                    menu_css_class='fa fa-fw fa-pause',
       
   322                                    view_name='wf-retiring.html',
       
   323                                    history_label=_("Retire request"),
       
   324                                    next_step=_("content managers authorized to take charge of your content are going "
       
   325                                                "to be notified of your request."),
       
   326                                    order=7)
       
   327 
       
   328 retiring_to_published = Transition(transition_id='retiring_to_published',
       
   329                                    title=_("Cancel retiring request"),
       
   330                                    source=RETIRING,
       
   331                                    destination=PUBLISHED,
       
   332                                    permission=MANAGE_CONTENT_PERMISSION,
       
   333                                    condition=can_cancel_operation,
       
   334                                    menu_css_class='fa fa-fw fa-mail-reply',
       
   335                                    view_name='wf-cancel-retiring.html',
       
   336                                    history_label=_("Retire request canceled"),
       
   337                                    order=8)
       
   338 
       
   339 retiring_to_retired = Transition(transition_id='retiring_to_retired',
       
   340                                  title=_("Retire content"),
       
   341                                  source=RETIRING,
       
   342                                  destination=RETIRED,
       
   343                                  permission=PUBLISH_CONTENT_PERMISSION,
       
   344                                  condition=can_manage_content,
       
   345                                  menu_css_class='fa fa-fw fa-stop',
       
   346                                  view_name='wf-retire.html',
       
   347                                  history_label=_("Content retired"),
       
   348                                  order=9)
       
   349 
       
   350 retired_to_archiving = Transition(transition_id='retired_to_archiving',
       
   351                                   title=_("Request archive"),
       
   352                                   source=RETIRED,
       
   353                                   destination=ARCHIVING,
       
   354                                   permission=MANAGE_CONTENT_PERMISSION,
       
   355                                   menu_css_class='fa fa-fw fa-archive',
       
   356                                   view_name='wf-archiving.html',
       
   357                                   history_label=_("Archive request"),
       
   358                                   next_step=_("content managers authorized to take charge of your content are going to "
       
   359                                               "be notified of your request."),
       
   360                                   order=10)
       
   361 
       
   362 archiving_to_retired = Transition(transition_id='archiving_to_retired',
       
   363                                   title=_("Cancel archiving request"),
       
   364                                   source=ARCHIVING,
       
   365                                   destination=RETIRED,
       
   366                                   permission=MANAGE_CONTENT_PERMISSION,
       
   367                                   condition=can_cancel_operation,
       
   368                                   menu_css_class='fa fa-fw fa-mail-reply',
       
   369                                   view_name='wf-cancel-archiving.html',
       
   370                                   history_label=_("Archive request canceled"),
       
   371                                   order=11)
       
   372 
       
   373 archiving_to_archived = Transition(transition_id='archiving_to_archived',
       
   374                                    title=_("Archive content"),
       
   375                                    source=ARCHIVING,
       
   376                                    destination=ARCHIVED,
       
   377                                    permission=PUBLISH_CONTENT_PERMISSION,
       
   378                                    condition=can_manage_content,
       
   379                                    action=archive_action,
       
   380                                    menu_css_class='fa fa-fw fa-archive',
       
   381                                    view_name='wf-archive.html',
       
   382                                    history_label=_("Content archived"),
       
   383                                    order=12)
       
   384 
       
   385 published_to_archived = Transition(transition_id='published_to_archived',
       
   386                                    title=_("Archive published content"),
       
   387                                    source=PUBLISHED,
       
   388                                    destination=ARCHIVED,
       
   389                                    trigger=SYSTEM,
       
   390                                    history_label=_("Content archived after version publication"),
       
   391                                    action=archive_action)
       
   392 
       
   393 retiring_to_archived = Transition(transition_id='retiring_to_archived',
       
   394                                   title=_("Archive retiring content"),
       
   395                                   source=RETIRING,
       
   396                                   destination=ARCHIVED,
       
   397                                   trigger=SYSTEM,
       
   398                                   history_label=_("Content archived after version publication"),
       
   399                                   action=archive_action)
       
   400 
       
   401 retired_to_archived = Transition(transition_id='retired_to_archived',
       
   402                                  title=_("Archive retired content"),
       
   403                                  source=RETIRED,
       
   404                                  destination=ARCHIVED,
       
   405                                  trigger=SYSTEM,
       
   406                                  history_label=_("Content archived after version publication"),
       
   407                                  action=archive_action)
       
   408 
       
   409 published_to_draft = Transition(transition_id='published_to_draft',
       
   410                                 title=_("Create new version"),
       
   411                                 source=PUBLISHED,
       
   412                                 destination=DRAFT,
       
   413                                 permission=CREATE_CONTENT_PERMISSION,
       
   414                                 condition=can_create_new_version,
       
   415                                 action=clone_action,
       
   416                                 menu_css_class='fa fa-fw fa-file-o',
       
   417                                 view_name='wf-clone.html',
       
   418                                 history_label=_("New version created"),
       
   419                                 order=13)
       
   420 
       
   421 retiring_to_draft = Transition(transition_id='retiring_to_draft',
       
   422                                title=_("Create new version"),
       
   423                                source=RETIRING,
       
   424                                destination=DRAFT,
       
   425                                permission=CREATE_CONTENT_PERMISSION,
       
   426                                condition=can_create_new_version,
       
   427                                action=clone_action,
       
   428                                menu_css_class='fa fa-fw fa-file-o',
       
   429                                view_name='wf-clone.html',
       
   430                                history_label=_("New version created"),
       
   431                                order=14)
       
   432 
       
   433 retired_to_draft = Transition(transition_id='retired_to_draft',
       
   434                               title=_("Create new version"),
       
   435                               source=RETIRED,
       
   436                               destination=DRAFT,
       
   437                               permission=CREATE_CONTENT_PERMISSION,
       
   438                               condition=can_create_new_version,
       
   439                               action=clone_action,
       
   440                               menu_css_class='fa fa-fw fa-file-o',
       
   441                               view_name='wf-clone.html',
       
   442                               history_label=_("New version created"),
       
   443                               order=15)
       
   444 
       
   445 archiving_to_draft = Transition(transition_id='archiving_to_draft',
       
   446                                 title=_("Create new version"),
       
   447                                 source=ARCHIVING,
       
   448                                 destination=DRAFT,
       
   449                                 permission=CREATE_CONTENT_PERMISSION,
       
   450                                 condition=can_create_new_version,
       
   451                                 action=clone_action,
       
   452                                 menu_css_class='fa fa-fw fa-file-o',
       
   453                                 view_name='wf-clone.html',
       
   454                                 history_label=_("New version created"),
       
   455                                 order=16)
       
   456 
       
   457 archived_to_draft = Transition(transition_id='archived_to_draft',
       
   458                                title=_("Create new version"),
       
   459                                source=ARCHIVED,
       
   460                                destination=DRAFT,
       
   461                                permission=CREATE_CONTENT_PERMISSION,
       
   462                                condition=can_create_new_version,
       
   463                                action=clone_action,
       
   464                                menu_css_class='fa fa-fw fa-file-o',
       
   465                                view_name='wf-clone.html',
       
   466                                history_label=_("New version created"),
       
   467                                order=17)
       
   468 
       
   469 delete = Transition(transition_id='delete',
       
   470                     title=_("Delete version"),
       
   471                     source=DRAFT,
       
   472                     destination=DELETED,
       
   473                     permission=MANAGE_CONTENT_PERMISSION,
       
   474                     condition=can_delete_version,
       
   475                     action=delete_action,
       
   476                     menu_css_class='fa fa-fw fa-trash',
       
   477                     view_name='wf-delete.html',
       
   478                     history_label=_("Version deleted"),
       
   479                     order=18)
       
   480 
       
   481 wf_transitions = [init,
       
   482                   draft_to_proposed,
       
   483                   retired_to_proposed,
       
   484                   proposed_to_canceled,
       
   485                   canceled_to_draft,
       
   486                   canceled_to_retired,
       
   487                   proposed_to_refused,
       
   488                   refused_to_draft,
       
   489                   refused_to_retired,
       
   490                   proposed_to_published,
       
   491                   published_to_retiring,
       
   492                   retiring_to_published,
       
   493                   retiring_to_retired,
       
   494                   retired_to_archiving,
       
   495                   archiving_to_retired,
       
   496                   published_to_archived,
       
   497                   retiring_to_archived,
       
   498                   retired_to_archived,
       
   499                   archiving_to_archived,
       
   500                   published_to_draft,
       
   501                   retiring_to_draft,
       
   502                   retired_to_draft,
       
   503                   archiving_to_draft,
       
   504                   archived_to_draft,
       
   505                   delete]
       
   506 
       
   507 
       
   508 @implementer(IContentWorkflow)
       
   509 class ContentWorkflow(Workflow):
       
   510     """PyAMS default content workflow"""
       
   511 
       
   512 
       
   513 @adapter_config(context=IContentWorkflow, provides=IWorkflowStateLabel)
       
   514 class WorkflowStateLabelAdapter(ContextAdapter):
       
   515     """Generic state label adapter"""
       
   516 
       
   517     @staticmethod
       
   518     def get_label(content, request=None, format=True):
       
   519         if request is None:
       
   520             request = check_request()
       
   521         translate = request.localizer.translate
       
   522         security = get_utility(ISecurityManager)
       
   523         state = IWorkflowState(content)
       
   524         state_label = translate(STATES_HEADERS[state.state])
       
   525         if format:
       
   526             state_label = state_label.format(principal=security.get_principal(state.state_principal).title)
       
   527         return state_label
       
   528 
       
   529 
       
   530 @adapter_config(name=DRAFT, context=IContentWorkflow, provides=IWorkflowStateLabel)
       
   531 class DraftWorkflowStateLabelAdapter(ContextAdapter):
       
   532     """Draft state label adapter"""
       
   533 
       
   534     @staticmethod
       
   535     def get_label(content, request=None, format=True):
       
   536         if request is None:
       
   537             request = check_request()
       
   538         translate = request.localizer.translate
       
   539         security = get_utility(ISecurityManager)
       
   540         state = IWorkflowState(content)
       
   541         if len(state.history) == 1:
       
   542             state_label = translate(STATES_HEADERS[state.state])
       
   543         else:
       
   544             state_label = translate(_('publication refused by {principal}'))
       
   545         if format:
       
   546             state_label = state_label.format(principal=security.get_principal(state.state_principal).title)
       
   547         return state_label
       
   548 
       
   549 
       
   550 wf = ContentWorkflow(wf_transitions,
       
   551                      states=STATES_VOCABULARY,
       
   552                      initial_state=DRAFT,
       
   553                      update_states=UPDATE_STATES,
       
   554                      readonly_states=READONLY_STATES,
       
   555                      protected_states=PROTECTED_STATES,
       
   556                      manager_states=MANAGER_STATES,
       
   557                      published_states=VISIBLE_STATES,
       
   558                      waiting_states=WAITING_STATES,
       
   559                      retired_states=RETIRED_STATES)
       
   560 
       
   561 
       
   562 @utility_config(name='PyAMS default workflow', provides=IWorkflow)
       
   563 class WorkflowUtility(object):
       
   564     """PyAMS default workflow utility"""
       
   565 
       
   566     def __new__(cls):
       
   567         return wf