SharePoint:扩展DVWP - 第25部分:通过SPServices创建列表项实现审计跟踪
回顾在本系列第14部分中,我们提到“如何将PreSaveAction()与jQuery配合使用”。PreSaveAction()允许我们在保存记录前做一些事情,当时我快速的列了一些在现实场景中可能会用jQuery做的事。我们举了一个审计跟踪的例子。当主列表发生变更时,向另一个列表中写入主列表的变更信息。最终实现的效果是我们有两块体现真实信息的地方:(1)当前视图(2)一个关于变更的历史记录,展示达到当前视图所经历的过程。
在本文中,我打算花一点时间剥开一层洋葱皮,看一下具体如何实现当列表项被创建,修改或删除时在另一个列表中写记录。
在列表B中创建一条来源与列表A的记录
有太多的项目中需要我们在列表中插入,编辑,或删除列表项,没准你现在进行的项目中就有类似的需求吧。或许你以前做过订单处理解决方案,其中订单产生后我们需要更新库存信息。又或许是一个图书借阅系统,当有人借出一本书时你需要更新该书的状态。
要给另一个列表更新信息我们有很多选择。但如果你是要创建一个条全新的列表项,就像审计跟踪例子中需要的那样,要面临哪些挑战呢?显然,你必要决定什么数据需要抓取到审计跟踪记录里。在主列表中的每一个需要记录变更的栏,都必须在审计列表中存在对应的栏。
从本例的设计初衷出发,我们会选取主列表中几乎所有的栏记录其变更,进行审计。
主列表——FTE(全日制雇员,Full-time Employees)
FTE的审计跟踪列表
其中的名称和顺序没必要完全吻合。对于本例来说,栏的对应关系如下:
主列表(显示名称| 内部名称) |
审计列表(显示名称| 内部名称) |
Location | @LocDept |
Location | @Location |
Group | @Group12 |
Group | @Group |
Position | @Positions |
Position | @Position |
Worker | @Title |
Employee | @Title |
Work Shift | @Workshift |
Work Shift | @WorkShift |
FTE | @FTE |
FTE |@FTE |
EffDate | @EffDate |
Effective Date | @EffDate |
Old Location | @Old%5Fx0020%5FLocation |
|
Last Change | @Audit |
|
|
Change Type | @ChangeType |
|
Month | @Month |
|
Year | @Year |
|
From/To | @FromTo |
创建者(Created By) 和 修改者(Modified By) 会由 SharePoint 自动填写,所以在两个列表中我们都不考虑这两个字段。
注意:两个列表的定义有以下几点不同:
- 在主列表中有一些查阅项类型的栏,对应到审计跟踪列表时变成了单行文本。
- 我在主列表中添加了一个Last Change栏。我们将用于保存最新一条审计记录的ID,并显示其修改时间字段。这里与Effective Date不同。
- 在审计列表中,我添加了:
- Change Type:一个选项型的栏(New,Update,Terminate,Resign,Transfer)——当一条列表项被插入,该值为New;对于编辑,如果Location发生了修改,则该值为Transfer;任何其他的修改都设为Update。如果列表项被删除,我们将记录是有公司解雇的(Terminate)还是有雇员提出辞职的(Resign)。【标注:在这里做一个记号,将来当我们讨论创建一个备用编辑模版时还会提及】
- From/To:选项型的栏(From,To)——用于显示记录变更前/后的视图,即一次修改会创建两条记录。
- Month:计算栏,用来取出Effective Date的月份,方便进行排序/分组。[公式: =TEXT([Effective Date],"mm – mmmm")]
- Year:计算栏,用来取出Effective Date的年,方便排序。 [公式:=TEXT([Effective Date],"yyyy")]
FTE 变更审计
回到jQuery部分的工作
这里的jQuery脚本也是来自与第14部分(有关变量的创建,请参考第13部分),其中用到了Marc Anderson的SPServices库:
function PreSaveAction() { var txtChangeType = "Update"; var txtNewName = $("input[name*=$ff1_]").val(); var txtOldName = $("span[id*=_ff9_]").text(); var txtNewPos = $("input[name*=$ff3_]").val(); var txtOldPos = $("span[id*=_ff11_]").text(); var txtNewShift = $("select[name*=$ff4_]").val(); var txtOldShift = $("span[id*=_ff12_]").text(); var txtNewFTE = $("input[name*=$ff5_]").val(); var txtOldFTE = $("span[id*=_ff13_]").text(); var txtNewGrp = $("input[name*=$ff2_]").val(); var txtOldGrp = $("span[id*=_ff10_]").text(); var txtNewLoc = $("input[name*=$ff6_]").val(); var txtOldLoc = $("span[id*=_ff14_]").text(); var txtEffDate = $("input[name*=$ff7_]").val(); if (txtNewLoc != txtOldLoc) txtChangeType = "Transfer"; $().SPServices({ operation: "UpdateListItems", async: false, listName: "FTE Change Audit", updates: "<Batch OnError='Continue' PreCalc='TRUE'>" + "<Method ID='1' Cmd='New'>" + "<Field Name='FromTo'>From</Field>" + "<Field Name='Title'>" + txtOldName + "</Field>" + "<Field Name='Location'>" + txtOldLoc + "</Field>" + "<Field Name='Group'>" + txtOldGrp + "</Field>" + "<Field Name='Position'>" + txtOldPos + "</Field>" + "<Field Name='WorkShift'>" + txtOldShift + "</Field>" + "<Field Name='FTE'>" + txtOldFTE + "</Field>" + "<Field Name='EffDate'>" + txtEffDate + "</Field>" + "<Field Name='ChangeType'>" + txtChangeType + "</Field>" + "</Method>" + "</Batch>", completefunc: function(xData, Status) { } }); $().SPServices({ operation: "UpdateListItems", async: false, listName: "FTE Change Audit", updates: "<Batch OnError='Continue' PreCalc='TRUE'>" + "<Method ID='1' Cmd='New'>" + "<Field Name='FromTo'>To</Field>" + "<Field Name='Title'>" + txtNewName + "</Field>" + "<Field Name='Location'>" + txtNewLoc + "</Field>" + "<Field Name='Group'>" + txtNewGrp + "</Field>" + "<Field Name='Position'>" + txtNewPos + "</Field>" + "<Field Name='WorkShift'>" + txtNewShift + "</Field>" + "<Field Name='FTE'>" + txtNewFTE + "</Field>" + "<Field Name='EffDate'>" + txtEffDate + "</Field>" + "<Field Name='ChangeType'>" + txtChangeType + "</Field>" + "</Method>" + "</Batch>", completefunc: function(xData, Status) { } }); return true; }; </script>
拆开来看
假设表单操作按钮已经被我们改为调用PreSaveAction(),那么这段脚本就会把变更保存至到审计列表。下面是整个过程的慢动作回放:
修改类型
var txtChangeType = "Update";
大多数的变更为Update类型。但是当Location发生修改时,类型就变成了Transfer(不考虑其他栏是否发生了修改...你的场景可能于此不同)。在我们设置所有的变量前,应对该项进行检查。
var txtNewLoc = $("input[name*=$ff6_]").val(); var txtOldLoc = $("span[id*=_ff14_]").text(); . . . if (txtNewLoc != txtOldLoc) txtChangeType = "Transfer";
Effective Date
由于Effective Date的变更只涉及到本身,所以这种情况下From和To两个变更版本是相同的。
SPServices
现在,我们得到了变更前的“旧”值和变更后的“新”值,接下来直接通过SPServices调用UpdateListItems创建两条记录就可以了。在上面的29行到41行,我们告诉函数创建一条新记录。然后,我们只需加载上CAML把值填入审计列表的相应的栏即可。
或者,我们可以用下面的语法替换updates中使用的CAML:
batchCmd: "New", valuepairs: [["FromTo", "From"], ["Title", txtOldName], ["Location", txtOldLoc], ["Group", txtOldGrp], ["Position", txtOldPos], ["WorkShift", txtOldShift], ["FTE", txtOldFTE], ["EffDate", txtEffDate], ["ChangeType", txtChangeType]],
注意到其中listName是目标列表的显示名称:
listName: "FTE Change Audit",
接下来对应FromTo="To"再重复一遍上面的过程。我们就完成了两条审计跟踪记录的创建。
插入和删除
你可以在新建时使用相同的技术,但如果用工作流来处理“新建项目时”会更容易些。如果你需要我对这两种方式进行详细讲解的话请发表评论。
我们会在将来讲到添加一个备用编辑模版时使用Resign和Terminate变更类型。
下一次:如果你需要在DVWP中从一个关联的列表更新数据该怎么办?在接下来的扩展DVWP系列中,我们会公布谜底。
参考资料
SharePoint:Extending the DVWP - Part 25:Using an Audit Trail by Creating List Items with SPServices