Dexterity开发手册:第六章 第11节 工作流

Dexterity开发手册:第六章 第11节 工作流
通过工作流来控制安全
在Plone中Workflow 用于三个方面:
  • 跟踪元数据,主要是跟踪一个对象的状态。
  • 创建内容审核流程,规范其他类型的处理
  • 管理对象安全
当写内容类型时,我们通常给该类型创建定制的工作流。本章我们将解释Plone工作流体系是怎么工作的,并给出一个简单的工作流样例。完整的工作流详细介绍不会在这里给出,我们仅仅介绍工作流的基本概义。这里并没有Dexterity特别的说明,工作流体系对于Archetypes 、Dexterity甚至直接的CMF体系都可以无差别的应用。


Plone内置的工作流系统是采用 DCWorkflow. 这是一个状态、事物系统,工作流开始于一个初始状态,然后通过事物(也叫触发动作)改变到其他状态。
当一个对象进入一个状态时(包括初始状态),工作流体系给出一个机会更新围绕该对象的相关权限。一个工作流管理大量的权限——典型的是这个核心的CMF 权限,象 View, Modify portal content 等等,并且在状态改变的对象上设置这些权限。注意,工作流体系是事件驱动,而不是实时的安全检查。仅仅当更改状态时,才更新安全信息。 这也是你要在ZMI点击 Update security settings ,来反映对已存的对象,工作流安全设置更新后的效果的原因。
一个状态能指派local 角色到 groups。或者说在Plone的共享tab,指派角色到组,但是角色到组的映射发生在每个状态改变时,十分象角色到权限的映射。因此你可以说在 pending_secondary 状态, 这个 Secondary reviewers 组的成员有 Reviewer 本地角色。角色到组的映射当和更通常的角色到权限的映射组合时,将是十分强大的事情。
状态更改将导致一些工作流变量被重新记录,诸如the actor (触发该事物(动作)的用户), the action (事物的名称), 日期和时间等等。这个变量列表是动态的,因此每个工作流能定义任意数量的变量链接到TALES表达式,在事物起作用的点上,这些变量被调用,计算当前的值。工作流也持续跟踪每个对象的当前状态。这个状态被揭露为一个特别类型的工作流变量,名称叫 state variable.。在Plone中大多数工作流用名称 review_state 表示这个变量。
每个状态变化时,工作流变量被记录在 workflow history中。 这个允许你查看当一个事物(动作)发生时,谁触发它,被影响的对象在此之前、之后是什么状态。In fact, the "current state" of the workflow is internally looked up as the most recent entry in the workflow history.
工作流变量也是工作清单的基础。这些基本上就是一些预定义的针对当前的工作流变量集合catalog 查询。 Plone的审核 portlet 为所有已安装的工作流显示所有当前的工作清单。这个速度有点慢, but it does meant that you can use a single portlet to display an amalgamated list of all items on all worklists that apply to the current user.大多数工作流有匹配review_state 变量的单个工作清单,例如,显示所有pending状态的内容。
如果说 states 是工作流系统中的静态部分,那么 transitions (actions) 是工作流系统中的动态部分。每个state 定义有0个或多个退出该状态的退出 transitions,并且每个transition 都精确定义一个目标状态,因此标记一个transition作为 t"stay in current state"是可行的。这种类型在下述情况下是有用的:如果你想在某种状态下做重复操作,又要记录这些操作历史,但不想改变对象的状态。

Transitions 被一个或更多的 guards控制。这些guards可以是权限 (优先采用the preferred approach), 角色 (mostly useful for the Owner role - in other cases it is normally better to use permissions) or TALES expressions。一个 transition 在所有guards条件都是true时,才是有效的。一个 transition如果没有任何 guard 条件,则意味着对所有人有效 (包括匿名用户)
缺省情况下Transitions 是被用户触发的,但也可自动触发。 An automatic transition triggers immediately following another transition provided its guard conditions pass. It will not necessarily trigger as soon as the guard condition becomes true, as that would involve continually re-evaluating guards for all active workflows on all objects!
When a transition is triggered, the IBeforeTransitionEvent and IAfterTransitionEvent events are triggered.他们是来自 Products.DCWorkflow 包的更低级别的事件,能告诉许多关于之前状态和当前状态的信息。在Products.CMFCore t中也有一个更高级别的事件IActionSucceededEvent , 通常用来反映一个工作流action已经完成。
In addition to the events, you can configure workflow scripts. 他们或者通过WEB创建,或者包装成 External Methods,可以被设置在一个transition完成前执行 (例如在对象进入目标状态之前)或者在完成以后执行 (对象已处于新的状态)。注意,如果采用event handlers, 你将需要检查这个event object以找出哪一个transition 被调用,因为事件是在所有transitions被触发。 The per-transition scripts are only called for the specific transitions for which they were configured.

Multi-chain workflows
工作流通过 portal_workflow 工具映射到内容类型。有一个缺省的工作流,由字符串 (Default).标识。某些类型没有工作流,这意味着他们没有状态信息,典型地从父亲对象继承权限。也有可能一个类型有多个工作流。每个工作流的名称用逗号分开,这叫工作流链。
Note that in Plone, the workflow chain of an object is looked up by multi-adapting the object and the workflow to the IWorkflowChain interface. The adapter factory should return a tuple of string workflow names (IWorkflowChain is a specialisation of IReadSequence, i.e. a tuple). The default obviously looks at the mappings in the portal_workflow tool, but it is possible to override the mapping, e.g. by using a custom adapter registered for some marker interface, which in turn could be provided by a type-specific behavior.
Multiple workflows applied in a single chain co-exist in time. Typically, you need each workflow in the chain to have a different state variable name. The standard portal_workflow API (in particular, doActionFor(), which is used to change the state of an object) also asumes the transition ids are unique. If you have two workflows in the chain and both currently have a submit action available, only the first workflow will be transitioned if you do portal_workflow.doActionFor(context, 'submit'). Plone will show all available transitions from all workflows in the current object's chain in the State drop-down, so you do not need to create any custom UI for this. However, Plone always assumes the state variable is called review_state (which is also the variable indexed in portal_catalog). Therefore, the state of a secondary workflow won't show up unless you build some custom UI.
在安全体系中,记住 role-to-permission (and group-to-local-role)的映射是事件驱动的,并且在每个 transition后被设置。 If you have two concurrent workflows that manage the same permissions, the settings from the last transition invoked will apply. If they manage different permissions (or there is a partial overlap) then only the permissions managed by the most-recently-invoked workflow will change, leaving the settings for other permissions untouched.

Multiple workflows can be very useful in case you have concurrent processes. For example, an object may be published, but require translation. You can track the review state in the main workflow and the translation state in another. If you index the state variable for the second workflow in the catalog (the state variable is always available on the indexable object wrapper so you only need to add an index with the appropriate name to portal_catalog) you can search for all objects pending translation, for example using a Collection.

Creating a new workflow
With the theory out of the way, let's show how to create a new workflow.
Workflows are managed in the portal_workflow tool. You can use the ZMI to create new workflows and assign them to types. However, it is usually preferable to create an installable workflow configuration using GenericSetup. By default, each workflow as well as the workflow assignments are imported and exported using an XML syntax. This syntax is comprehensive, but rather verbose if you are writing it manually.
For the purposes of this manual, we will show an alternative configuration syntax based on spreadsheets (in CSV format). This is provided by the collective.wtf package. You can read more about the details of the syntax in its documentation. Here, we will only show how to use it to create a simple workflow for the Session type, allowing members to submit sessions for review.
To use collective.wtf, we need to depend on it. In setup.py, we have:
install_requires=[ ... 'collective.wtf', ],
As before, the <includeDependencies /> line in configure.zcml takes care of configuring the package for us.
A workflow definition using collective.wtf consists of a CSV file in the profiles/default/workflow_csv directory, which we will create, and a workflows.xml file in profiles/default which maps types to workflows.
The workflow mapping in profiles/default/workflows.xml looks like this:
<?xml version="1.0"?> <object name="portal_workflow"> <bindings> <type type_id="example.conference.session"> <bound-workflow workflow_id="example.conference.session_workflow"/> </type> </bindings> </object>The CSV file itself is found in profiles/default/workflow_csv/example.conference.session_workflow.csv. It contains the following, which was exported to CSV from an OpenOffice spreadsheet. You can find the original spreadsheet with the example.conference source code. This applies some useful formatting, which is obviously lost in the CSV version.
为你自己的工作流,你可以采用这个模板作为开始。
"[Workflow]" "Id:","example.conference.session_workflow" "Title:","Conference session workflow" "Description:","Allows members to submit session proposals for review" "Initial state:","draft" "[State]" "Id:","draft" "Title:","Draft" "Description:","The proposal is being drafted." "Transitions","submit" "Permissions","Acquire","Anonymous","Authenticated","Member","Manager","Owner","Editor","Reader","Contributor","Reviewer" "View","N",,,,"X","X","X","X",, "Access contents information","N",,,,"X","X","X","X",, "Modify portal content","N",,,,"X","X","X",,, "[State]" "Id:","pending" "Title:","Pending" "Description:","The proposal is pending review" "Worklist:","Pending review" "Worklist label:","Conference sessions pending review" "Worklist guard permission:","Review portal content" "Transitions:","reject, publish" "Permissions","Acquire","Anonymous","Authenticated","Member","Manager","Owner","Editor","Reader","Contributor","Reviewer" "View","N",,,,"X","X","X","X",,"X" "Access contents information","N",,,,"X","X","X","X",,"X" "Modify portal content","N",,,,"X","X","X",,,"X" "[State]" "Id:","published" "Title:","Published" "Description:","The proposal has been accepted" "Transitions:","reject" "Permissions","Acquire","Anonymous","Authenticated","Member","Manager","Owner","Editor","Reader","Contributor","Reviewer" "View","Y","X",,,,,,,, "Access contents information","Y","X",,,,,,,, "Modify portal content","Y",,,,"X","X","X",,, "[Transition]" "Id:","submit" "Title:","Submit" "Description:","Submit the session for review" "Target state:","pending" "Guard permission:","Request review" "[Transition]" "Id:","reject" "Title:","Reject" "Description:","Reject the session from the program" "Target state:","draft" "Guard permission:","Review portal content" "[Transition]" "Id:","publish" "Title:","Publish" "Description:","Accept and publish the session proposal" "Target state:","published" "Guard permission:","Review portal content"这里可以看到几个状态和事务。每个状态都包含一个 role/permission 映射,和可能的推出事务(动作)列表。每个事务都包含一个目标状态和有些其他的元数据如标题、描述等,以及触发权限。
注意这儿不象大多数的 GenericSetup import 步骤,权限用id,在工作流体系中,引用的权限用Zope 2 权限的title 。
当包被重新安装时,这个工作流在Portal_workflow 应该有效,并被映射到 Session 类型。
如果你有一个已存的实例,不要忘记去到 portal_workflow页面点击Update security settings,以确保已存的对象能反映最新的工作流安全设置更改。
关于添加权限的注意
这个工作流加到标准的会员能添加Session proposals 到 Programs,然后将被审核。之前我们授予example.conference: Add session 权限到 Member 角色。这是必须的,但是还不足够让memers能加sessions 到 programs。这用户还将需要通常的c Add portal content 权限在 Program 文件夹。
有两种方法来实现这个:
  • Program类型建立工作流来管理这个权限。
  • 用这个共享tab授予 Can add 权限到 Authenticated Users组。这个授予Contributor 本地角色到Members。缺省情况下,这个角色被授予Add portal content 权限。
设置