Orchard Core 使用工作流处理审批和创建内容项
译自:http://www.ideliverable.com/blog/orchard-core-workflows-walkthrough-content-approval
转载请注明出处, 原文地址:https://www.cnblogs.com/Qbit/p/orchardcore-Workflow.html
为了更好地了解新的Workflows模块,我们将创建一个工作流程来尝试一些新的活动。让我们创建一个内容审批工作流程,为以下流程建模:
- 作为作者,我可以向某个HTTP端点提交新文章。
- 网站管理员会收到一封电子邮件,表明已提交新文章以供审核。
- 当审核人员批准该文章时,将创建并发布实际的文章内容项。作者会收到一封电子邮件通知,其中包含指向已发布内容项的链接。
- 当审核人员 拒绝 时,主持人会收到一封电子邮件通知,其中包含要向其提交工作更新版本的新URL。
- 作者必须在超时到期之前提交她的工作的修订版本。
- 当作者提交修订版本时,主持人再次可以选择批准或拒绝该文章,允许工作流程无限期地重复
让我们一步一步地构建这个工作流程,看看它的实际效果。
完整的工作流程如下所示:
Step 1:启用工作流程和电子邮件功能
转到配置 - > 模块并启用以下3个功能:
- HTTP Workflow Activities
- Timer Workflow Activities
Step 2: 创建一个新的工作流定义
管理菜单将出现一个新的项,其中显示“Workflows”。 单击此项并单击“Create Workflow Type”按钮。为工作流命名为“Content Approval Workflow”,并确保勾选Enabled复选框。另外两个复选框可以保持未选中状态,但可以随意尝试。
第3步:添加传入HTTP请求事件
我们需要一个允许作者将内容发布到我们的应用程序的工作流程,因此我们将通过公开一个可以启动的HTTP端点来启动我们的工作流程。单击“ Add Event” 按钮以启动活动选择器。
从活动选择器中,查找HTTP Request,然后单击“ Add” 按钮。
在活动编辑器中,选择POST作为HTTP方法,然后单击Generate按钮。单击此按钮将生成工作流URL和SAS令牌。
在向生成的URL发出HTTP请求时必须提供此令牌,否则将不会执行工作流。此令牌的目的是防止未授权方触发您的工作流程。
因此,重要的是不要将此URL分发给任何人; 仅将工作流URL提供给您信任的各方,因为拥有此URL的任何人都可以调用您的工作流。
单击“ 保存”按钮。
Step 4: 设置开始活动
返回工作流编辑器画布,单击添加的HTTP Request活动。
当你这样做时,你会发现活动下方会出现一个小工具栏。
单击最左侧的按钮,使用“打开/关闭电源”图标将此活动标记为启动活动。
只有标记为启动活动的活动才能导致工作流程执行。标记位启动活动后该活动的背景色将变为绿色如下图:
Step 5: 添加Set Property Activity
当作者发出HTTP POST请求时,我们希望她发送JSON格式的内容。为了处理这些数据,我们将其存储为工作流属性。
单击“ Add Task ”按钮,查找“ Set Property Activity”活动,然后单击“ Add”。在“ 名称”字段中输入“Article”,在“ 值”字段中输入以下JavaScript表达式:
JSON.parse(readBody())
上述readBody函数读取从请求作为字符串的HTTP内容,并且JSON.parse功能解析这个字符串转换成一个实际的对象。
Set Property活动将此对象存储为名为“Article”的属性。这允许我们通过简单地询问此属性来在整个工作流程的其余部分中访问此对象。
我们将在下一步看到它是如何工作的。供您参考,我打算将JSON字符串看起来像这样:
{ "author": { "name": "Max Whitaker", "email": "max@whitaker.me" }, "article":{ "title": "A short story", "body": "The article content goes here" } }
在进行下一步之前,请确保将HTTP Request活动与Set Property活动相关联。您的工作流现在应该如下所示:
Step 6: 添加发送电子邮件活动
当我们收到作者提交的文章时,我们会通知网站的主持人。我们通过发送电子邮件来做到这一点。使用以下值添加“发送电子邮件”活动:
Sender: workflow@acme.io
Recipients: moderator@acme.io
Subject: New article submission received: {{Article.article.title}}
Body:
<p>Hi! A new article was just submitted by <strong>{{Article.author.name}}</strong>.</p>
<p><em>{{Article.article.body}}</em></p>
<p>you have the following available actions:</p>
<p>
<a href="{{"Approve" | signal_url | absolute_url}}">Approve</a> | <a href="{{"NeedsWork" | signal_url | absolute_url}}">Needs Work</a>
</p>
请注意,我们可以在Subject和Body字段中使用Liquid语法。Orchard Core Workflow的一个非常巧妙的功能是它将为Liquid模板上下文提供所有工作流属性。由于我们在上一步中添加了“Article”属性,因此我们现在可以在编写Liquid语句时直接访问该属性。由于我们将JSON内容解析为实际对象,因此我们可以简单地使用点表示法来访问其任何字段。
作为电子邮件的一部分,我们希望为主持人提供两个操作:批准和 需要工作。我们将通过使用signal_url Liquid过滤器生成工作流URL来实现此目的。此过滤器的工作方式是它将获取其输入(前面的字符串,例如“Approve”或“NeedsWork”)并将其用作信号名称。然后将该信号名称编码为SAS令牌,并作为查询字符串参数附加到URL。调用此完整URL将触发Signal事件。然后将执行以配置有相应信号名称的Signal事件开始的任何工作流程。并且将恢复由具有相应信号名称的Signal事件阻止的任何工作流程。这正是我们工作流程所需要的。
确保将“Set Article Property ”活动与“发送电子邮件”活动相关联。
Step 7:添加Fork活动
Fork活动将工作流分成多个并行执行路径。
我们在这里想要这样做的原因是我们将使用两个Signal事件并等待它们中的任何一个被触发。Signal事件将导致工作流程执行暂停,直到触发为止。
我们可以采用不同的方法并简单地添加另一个HTTP请求事件,例如,期望具有查询字符串参数的请求包含审批人批准或拒绝该文章,并简单地测试该值。
虽然这样可以很好地工作,但Signal事件和HTTP Request事件之间的主要区别在于Signal事件允许您将信号名称包含在SAS令牌中,这有效地防止了任何人篡改信号名称。
添加Fork活动时,请输入以下两个结果:“Await Approval,Await Needs Work”,然后单击“保存”。在工作流编辑器画布上,确保将“发送电子邮件”活动的“完成”结果连接到新添加的Fork活动。
请注意Fork 活动上面的两个绿色小点,一个小点代表一个分支,这个分支的数量与我们定义的结果数量相匹配,多个结果之间使用逗号分割
Step 8:添加两个信号活动
打开事件活动选择器并添加Signal事件。使用“Approve”作为信号名称配置此项。添加另一个Signal事件,这次是针对“NeedsWork”信号。添加两个活动后,将Fork活动中的相应结果连接到Signal活动。
Step 9:添加加入活动
此时,两个信号事件可以继续工作流程。如果触发“批准”信号,则执行将从该点开始继续,如果触发“NeedsWork”信号,则从该点开始执行。但是,我们不想要的是,如果其中一个信号被触发,另一个信号仍然可以在以后触发。相反,当触发一个信号时,我们希望将两个分支“连接”到一个执行路径中。为此,请添加Join活动并将其Mode设置为WaitAny。
第10步:添加If / Else活动
虽然执行已合并,但我们确实需要知道触发了哪个信号,以便我们采取适当的措施。如果触发了“Approve”信号,我们希望继续创建内容项。但如果“NeedsWork”信号被触发,我们想采取不同的路径。虽然我们可以在工作流上设置属性以响应Signal事件,但有一种更简单的方法:每当触发信号事件时,信号名称将作为工作流输入提供。让我们继续添加If / Else活动并将其条件设置为:
input("Signal") === "NeedsWork"
如果接收到的信号为“NeedsWork”,则JavaScript表达式的计算结果为true;如果是任何其他信号,则表达式为false。由于我们在工作流程中此时仅支持两个信号,因此该逻辑工作正常。如果您有一个等待两个以上信号的工作流程,您应该使用 脚本活动,它可以让您定义多个结果。
有两种可能的结果:无论是真还是假。我们将首先进行虚假结果(这意味着信号不是“NeedsWork” 就是 “Approve”),然后回到工作流程中的这一点并继续使用True路径(“文章需要工作”)。
第11步:创建文章内容类型
在我们真正让工作流创建文章内容项之前,我们首先需要定义文章内容类型。为此,请转到“ 内容定义” - >“ 内容类型”,然后单击“ 创建新类型”按钮。为“显示名称”和“技术名称”字段输入“Article”。
接下来,添加以下部分:
- Autoroute
- Body
- Title
还要添加一个文本字段,并为其指定显示名称“作者”和技术名称“AuthorName"。您的内容定义应如下所示:
现在我们已经有了Article内容类型,我们可以返回到工作流编辑器并继续下一步。
第12步:添加“创建内容活动”
如果提交的文章(JSON有效负载)得到主持人的批准,我们需要工作流实际创建文章内容项并复制提供的内容。为此,我们将利用新的“创建内容”活动。继续并从活动选择器添加它,并提供以下值:
内容类型:文章
发布:是的
内容属性:
{
"TitlePart": {
"Title": "{{ Article.article.title }}",
},
"BodyPart": {
"Body": "{{ Article.article.body }}"
},
"Article": {
"AuthorName": {
"Text": {{Article.author.name | json | raw}}
}
}
}
注意此处提交的内容如果包含中文您应当使用json | raw 过滤器 ,因为页面提交过来的所有内容将会被转换为HTML编码格式 ,
它将自动生成 外围的双引号,所以不需要再添加双引号包裹 ,其它位置的Liquid 类似,当然您也可以去掉 json filter 它将不会生成双引号,但Orchard Core的作者建议您 结合 json 过滤器一起使用,因为后续版本可能会修复这个编码显示的问题
"Text": {{ Article.author.name | json | raw }}
“内容属性”字段的值需要包含一个JSON结构,该结构与用于ContentItem对象的Content属性的结构完全相同。有许多方法可以确定内容项的结构。
比如:
- 一种方法是通过管理员创建一条内容项,然后进入数据库 Document 表并查看其JSON结构。
- 另一种方法是使用Visual Studio调试器并在Display操作方法的Contents模块的AdminController中设置断点,然后使用立即窗口写出contentItem的Content属性。我使用后一种方法,因为我碰巧在调试器中。
请注意,我正在使用Liquid语法从Article工作流属性中插入字段。
Step 13: 添加发送邮件活动
当主持人批准提交的文章时,让作者知道这一点会很好。我们通过电子邮件来做到这一点。添加另一个发送电子邮件活动并提供以下值:
Sender: workflow@acme.io
Recipients: {{ Article.author.email }}
Subject: Your article has been approved and published!
Body:
<p>Dear {{ Article.author.name }},</p>
<p>Your article "<strong>{{ Article.article.title }}</strong>" has been published!</p>
<p>You can check it out online via the following link:</p>
<p><a href="{{ LastResult | display_url | absolute_url}}">{{LastResult | display_text }}</a></p>
这是“批准”分支所需的最后一项活动。执行此活动后,工作流程即完成。
Step 14:添加另一个发送电子邮件活动
返回If / Else活动,我们将“已批准”流程分支。当主持人触发“NeedsWork”信号时,此活动的条件将评估为真,这意味着我们需要让作者知道他的工作不够好。为此,请使用以下字段添加另一个“发送电子邮件”活动:
Sender: workflow@acme.io
Recipients: {{Article.author.email}}
Subject: Your article needs work
Body:
<p>Dear {{Article.author.name}},</p>
<p>We received your submission, thank you!</p>
<p>However, your article needs a little bit of work.</p>
<p>When you're ready, please submit (HTTP PUT) an updated draft to the following URL:</p>
<p><strong>{{ "/Workflows/Invoke?token=CfDJ8J14khzBx5ZDmlhyDVsxlZ4hZHnM7Qfw4IqnSFuIMmcp1uwL90Gcf9uokBJSe_F5fgmCODds-YblHBCH1dnJ8UnwkL8TaYR3BZsDUXH6bBRD0_ek_Vgcj9aHIcd9YhRI6UkV15g6hu-ui8_TDQGPjlhGwziTzerc0Yee3CFAs_6YOktpZH4dR1K4GjFs5XTzXprY22L1V2ddNOvRhkePmHXPMunZ2XE_3Ioi1w0FU_00" | absolute_url }}</strong></p>
<p></p>
<p><strong>NOTE:</strong> Please re-submit your work within the next hour, or else your submission will expire!</p>
(重要提示:使用在下面的步骤17中为HTTP请求事件生成的URL替换上面代码段中的URL!)。
Step 15:添加Fork活动
我们现在想等待作者的修改工作,所以我们应该添加另一个HTTP Request事件。但是,如果作者未在特定时间内向我们发送更新,我们还希望添加某种“超时”,以使工作流程到期。为了实现这个逻辑,我们将等待两个事件之一:HTTP Request事件和Timer事件。由于我们想要同时等待这两个事件,我们应该再次分配我们的工作流,所以继续添加一个新的Fork活动并指定以下两个结果:“Await Revision,Begin Timer”
Step 16:添加定时器活动
要实现超时,请将Timer事件添加到工作流。Timer actvity使用CRON选项卡表达式来控制它何时应该触发。让我们做5分钟,转换为以下CRON选项卡表达式: */5 * * * *。
Step17:添加HTTP请求活动
我们希望作者通过发送HTTP PUT请求来提交修改后的文章,所以让我们添加另一个HTTP Request事件。确保将HTTP方法设置为Put。只要作者发出的HTTP请求与配置的任何方法匹配,您使用哪种HTTP方法并不重要。确保为此活动生成URL,并在步骤14中描述的电子邮件正文中使用它。
记得回到第14步更新URL
Step 18:添加加入活动
出于与步骤9中描述的相同的原因,我们需要添加Join活动以将分叉流连接回单个执行路径,因为我们只想在定时器触发或收到HTTP请求时继续执行,但是从来没有。
如果没有Join活动,即使我们在此之前收到HTTP请求,计时器仍会在5分钟后触发。所以让我们继续使用WaitAny模式添加Join活动。和往常一样,确保连接点。
Step19:添加If / Else活动
虽然我们将两个流连接成一个流,但我们需要知道触发了哪个事件:HTTP请求或计时器。幸运的是,这很容易做到,因为当触发Timer事件时,它会将工作流的LastResult 属性设置为字符串文字“TimerEvent”。所以我们需要做的就是添加一个If / Else活动并输入一个JavaScript表达式来检查这个astResult属性。表达式如下:
lastResult() === "TimerEvent"
如果该条件的计算结果为true,则表示计时器已被触发且作者的时间已到。发生这种情况时,我们应该给他发一封电子邮件。另一方面,如果作者提交了更新的文章,我们应该重复工作流程并向主持人发送电子邮件,以便关闭循环。
Step 20:添加发送电子邮件活动
首先,使用以下值添加另一个“发送电子邮件”活动:
Sender: workflow@acme.io
Recipients: {{Article.author.email}}
Subject: Sorry, you're revision took too long and we cancelled your submission
Body:
<p>亲爱的{{Article.author.name}},</ p>
<p>不幸的是,您的修订时间窗口已过期,我们不得不取消您的提交。</ p>
<p>请不要气馁。您可以随时通过提交新文章重新开始提交过程。</ p>
<p>很快见到您!</ p>
Step 21: 关闭循环
最后一步是关闭循环:当作者提交更新的文章时,我们需要重复整个工作流程。我们需要做的就是将步骤19中的If / Else活动的假结果连接到步骤5中创建的Set Article Property活动。
注意:当我尝试将If / Else活动与Set Property活动一直连接到顶部时,我对编辑器不会自动向上滚动这一事实感到困惑。要解决此问题,我缩小了浏览器窗口。或者,您可以暂时将源活动拖动到足够接近目标活动,连接两者,然后将源活动拖回到您希望的活动。
经过上面一番操作,此时你的工作流画布大概长这样:
下面的内容就直接机翻了,因为我打算在下一篇文章中使用页面提交,而不是用 Postman,当然原理差不多。
演示:试一试!
是时候尝试工作流程了!
要触发我们的工作流,我们需要发出一个HTTP POST请求,其中包含JSON格式的文章信息。一个简单的方法是使用像Postman这样的工具,这是我用于此演示的工具。因此,使用以下值在Postman中创建新请求:
(请务必使用您的启动活动所产生的实际URL)
方法: POST
身体:
{
“author”:{
“name”:“Max Whitaker”,
“email”:“max@whitaker.me”
},
“article”:{
“title”:“短篇小说”,
我讨厌你,我想要一个wifi代码。安迪回过头来,更加自信,仍然指着那些微不足道的报纸。“佐伊,我没有钱,”他回答道。他们带着闪闪发光的感觉看着对方,就像两个苦涩,强壮的獾在一个非常冷酷的晚宴上跳过,这个晚宴上有独立的音乐,还有两个高贵的叔叔在节拍中蹦蹦跳跳。安迪认为佐伊英俊的脚和骨瘦如柴的睫毛。'我有同样的感觉!' 安迪高兴地露出了笑容。佐伊看起来很担心,她的情绪像一个刺耳的,破烂的岩石一样脸红。然后佐伊进来一个漂亮的港口。结束” 他们带着闪闪发光的感觉看着对方,就像两个苦涩,强壮的獾在一个非常冷酷的晚宴上跳过,这个晚宴上有独立的音乐,还有两个高贵的叔叔在节拍中蹦蹦跳跳。安迪认为佐伊英俊的脚和骨瘦如柴的睫毛。'我有同样的感觉!' 安迪高兴地露出了笑容。佐伊看起来很担心,她的情绪像一个刺耳的,破烂的岩石一样脸红。然后佐伊进来一个漂亮的港口。结束” 他们带着闪闪发光的感觉看着对方,就像两个苦涩,强壮的獾在一个非常冷酷的晚宴上跳过,这个晚宴上有独立的音乐,还有两个高贵的叔叔在节拍中蹦蹦跳跳。安迪认为佐伊英俊的脚和骨瘦如柴的睫毛。'我有同样的感觉!' 安迪高兴地露出了笑容。佐伊看起来很担心,她的情绪像一个刺耳的,破烂的岩石一样脸红。然后佐伊进来一个漂亮的港口。结束” rabblesnatching rock。然后佐伊进来一个漂亮的港口。结束” rabblesnatching rock。然后佐伊进来一个漂亮的港口。结束”
}
}
内容类型: application / json
我们还需要配置SMTP服务器,以便我们可以让工作流实际发送电子邮件。但我没有使用真正的SMTP服务器,而是使用Smtp4Dev,这是一个实际上不发送电子邮件的SMTP服务器; 相反,它显示在列表中发送的电子邮件,以便我们可以检查发送的消息。这样我们就可以发送电子邮件到假地址,并仍然检查发送的消息。我将使用Smtp4Dev进行此演示。
如果您还没有这样做,请确保转到配置 - >设置 - > Smtp并提供SMTP详细信息。在我的情况下,我配置Smtp4Dev来侦听端口2525,所以我的设置如下所示:
提交HTTP请求时,您应该收到HTTP 200 Accepted状态代码。我们还应该看到工作流的工作流实例:
单击工作流实例ID以转到工作流实例详细信息视图,该视图应如下所示:
请注意以下事项:
- 工作流处于暂停状态(1)。
- 两个Signal活动阻止了工作流程(2)(3)
当我们在这个页面上时,让我们看一下工作流实例的State视图:
如您所见,提交的JSON存储在名为“Article”的自定义属性中。
现在让我们打开Smtp4Dev Web界面来检查发送的电子邮件:
所以这很有效。现在让我们点击电子邮件中的“需要工作”链接。当您这样做时,您应该会立即看到一封新电子邮件:
辉煌!我们还来看看工作流程的当前状态:
如您所见,工作流现在在HTTP Request事件(1)和Timer事件(2)上暂停。如果我们等待足够长的时间(大约5分钟),我们将收到一封电子邮件,告知我们的时间已到。(等待5分钟......)果然:
在我们发送更新的文章之前触发了计时器,并导致工作流程结束。
要再次启动工作流,我们需要发送一个新的HTTP POST请求,然后批准或请求修订等。您明白了。试试看!
结论
我希望我已经很好地了解了新的工作流程模块的功能以及它的工作原理。虽然该模块已经在其当前形状中实现了强大的工作流程,但还有更多功能,例如:
- 更多活动,例如用户,角色,内容部署,表单,工作流活动和许多其他活动
- 工作流跟踪
- 编辑和活动用户体验改进
- 版本
您可以在此处关注GitHub上的Workflows项目:https: //github.com/OrchardCMS/OrchardCore/projects/4
如果您对新功能有任何建议,请告诉我们!您可以 使用 @sfmskywalker 在Gitter找到我,给我发电子邮件或者只是 在GitHub上创建一个问题。
Orchard Core 工作流相关介绍:https://orchardcore.readthedocs.io/en/dev/OrchardCore.Modules/OrchardCore.Workflows/
下一篇文章中,我将介绍如何使用Page 配合本次演练的工作流进行内容提交。
Orchard Core QQ 群:877196442