利用 LotusScript 实现 Microsoft Word 文档在公文流转中的公文留痕
Domino 和 Word 的集成
简介: 在大部分的 OA(Office Automation)系统中都有涉及公文流转的模块,而这些模块往往是整个 OA 系统的核心模块,在公文流转中有一项需求特别多,就是要将公文在流转中的“痕迹”(比如是谁在什么时候增加,删除或者修改了文档的哪一部分,领导的意见,批注等)保留下来,供公文流转过程中的各种人员参考。本文将介绍利用 LotusScript 实现 Microsoft Word 文档在公文流转中的公文留痕的方法。
Object Linking and Embedding (OLE) 对象
OLE 技术,全称为对象连接与嵌入技术,是一种面向对象的技术,通过定义和实现应用程序作为对象彼此连接的机制,完成应用程序之间的集成。OLE 是在客户应用程序间传输和共享信息的一组综合标准,基于组件对象模型(COM),现在已经广泛应用于电子表格、字处理、财务软件、项目管理软件等等。
在 LotusScript 中要创建或者得到 OLE 对象,需要使用 CreateObject 函数或者 GetObject 函数。
CreateObject 函数
创建一个指定类型的 OLE 对象
语法
CreateObject ( className )
参数
className 是一个 appName.appClass 格式的字符串,表示要创建的对象的类型,比如“WordPro.Application”。appName 表示支持 OLE 的应用程序名,appClass 表示要创建的对象的类型
返回值
返回一个 OLE 对象的引用
用法
在 LotusScript 中要用 Set 来把 CreateObject 函数返回的对象引用赋值给一个变体类型(Variant)的变量。
如果该类型的应用程序还没有运行,CreateObject 函数会先启动这个应用程序然后再创建 OLE 对象。OLE 对象引用只在应用程序运行时有效,如果在使用 OLE 对象引用时应用程序退出了,LotusScript 将抛出一个运行时错误。每一种 OLE 对象都有其自己定义和提供的一套类,通过这些类就可以操作该种应用程序。
示例
Sub Initialize '创建一个 Word.Application 对象 Set MyApp = CreateObject ( "Word.Application") '设置该对象的 Visible 属性为 True MyApp.Visible = True End Sub |
GetObject 函数
打开一个应用程序文件包含的 OLE 对象或者返回一个当前活动的指定类型的 OLE 对象。
语法
GetObject ( [pathName] [ , className ] )
参数
pathName 一个包含全路径文件名的应用程序文件或者为空。该应用程序必须支持 OLE。如果为空字符串 (“”) 或者省略,则必须制定 className,并且会返回当期活动的对象。
className 同 CreateObject 的 className 参数。但是可以省略,如果省略,则更具 pathName 来决定返回的 OLE 对象。
返回值
返回一个 OLE 对象的引用
用法
同 CreateObject
示例
Sub Initialize Dim myDoc As Variant '从文件得到 WordPro.Document 对象。 Set mydoc = getobject("d:\wordpro\docs\test.lwp","WordPro.Document") ' 调用 WordPro.Document 对象的 Print 方法。 myDoc.Print End Sub |
实现 Microsoft Word 文档在公文流转中的公文留痕
下面通过一个例子来展示如何利用 LotusScript 来实现 Microsoft Word 文档在公文流转中的公文留痕。本例中用户首先建立一个发文稿,正文为 Word 文档,发稿人写完正文后以附件形式保存,然后提交审核,审核过程中将会实现留痕,审核完毕后将形成正式公文,以后可以查看正文但是不能修改,查看方式可以显示或者隐藏修订的信息。本例中为了让读者看起来方便,没有加任何错误处理的代码,实际应用中肯定需要加上这种代码来保证出错的时候能然 Word 程序退出和临时文件的清除。
首先创建一个空白的 WordTest.nsf,建立一个表单名为“发文”,别称为“FaWen”,为了简化,此表单中只包含三个域:Status,Title,Body。Status 表示公文流转的状态,各个表单按钮根据 Status 的值来隐藏或者显示,Status 本身总是隐藏的,Title 表示本发文的标题,Body 保存 Word 正文,总是隐藏,以免用户不通过表单按钮来操作 Body 域中的 Word 正文。本例中实现了一个简单的公文流转的流程,包括公文起草,审批和审批完毕后的查看,所有代码都在发文表单的操作中完成。下面先看看起草正文的代码。
清单 1
Sub Click(Source As Button) Dim activedoc As Variant Dim WordApp As Variant Dim s As New NotesSession Dim ws As New NotesUIWorkspace Dim db As NotesDatabase Dim uidoc As NotesUIDocument Dim doc As NotesDocument Set db = s.CurrentDatabase Set uidoc = ws.CurrentDocument Set doc = uidoc.Document Dim rtitem As NotesRichTextItem Set rtitem = doc.GetFirstItem("Body") If rtitem Is Nothing Then Set rtitem = New NotesRichTextItem(doc, "Body") End If '定义文件名和全路径文件名 Dim sFileName As String, sFilePath As String '根据标题来确定正文文件名 sFileName = Trim(uidoc.FieldGetText("Title"))+".doc" If sFileName = ".doc" Then Msgbox "请输入标题" Call uidoc.GotoField("Title") Exit Sub End If sFilePath = "C:\\"+sFileName '创建 Word 对象 Set WordApp=CreateObject("Word.Application") WordApp.Visible=True '打开或者新建正文 If Not OpenWordDoc(WordApp, uidoc, sFileName) Then Exit Sub '激活当前的文档 Set activedoc = WordApp.ActiveDocument '设置 Word 文档的作者为当前用户 WordApp.Username = s.CommonUsername WordApp.Activate activedoc.Activate '最大化 Word 窗口 WordApp.WindowState =1 '编辑完成后保存并退出 Word Msgbox "请单击确定完成编辑",32,"消息" '保存正文并退出 Word Call activedoc.SaveAs(sFilePath) Call activedoc.Close(0) Call WordApp.Quit(0) Set activedoc = Nothing Set WordApp = Nothing '附上修改后的正文 Set rtitem = New NotesRichTextItem(doc, "Body") Call rtitem.EmbedObject( EMBED_ATTACHMENT , "" , sFilePath ) doc.SaveOptions = 0 Call uidoc.Close doc.SaveOptions = 1 doc.Status = "草稿" doc.Form = "FaWen" Call doc.Save(True, True) '删除在硬盘的临时文件 Kill sFilePath Call ws.EditDocument(False, doc) Msgbox "正文起草完毕" End Sub |
在本例中,根据标题来确定 Word 正文文件名,然后创建 Word 对象并设置 Word 可见,然后调用 OpenWordDoc() 函数来新建或者打开已经存在于 Body 域的 Word 文档,然后将当前 Notes 用户名传给 Word 作为 Word 文档的作者并激活 Word 程序,然后下面的这句很重要,它相当于暂停了程序让用户能在 Word 文档中编写发文的正文,等编辑完成后点击确定按钮让后面的程序继续执行。
Msgbox "请单击确定完成编辑",32,"消息" |
确定完成编辑后,通过调用 VBA 相关的方法保存该 Word 文档并退出 Word 程序,然后将此正文嵌入到 Body 域,最后删除留在硬盘的临时文件。
下面是 OpenWordDoc() 函数的实现:
清单 2
Function OpenWordDoc(WordApp As Variant, uidoc As notesuidocument, sFileName As String) As Boolean OpenWordDoc=True Dim doc As notesdocument Set doc = uidoc.Document '如果已经有了正文附件,直接打开修改,否则根据状态新建一个空的 Word 文件或者退出 Dim attachment As NotesEmbeddedObject Set attachment = doc.GetAttachment(sFileName) If Not attachment Is Nothing Then If Dir$( attachment.Name)<> "" Then Kill "C:\\"+sFileName '将正文附件解开到硬盘并打开 Call attachment.ExtractFile("C:\\"+sFileName) Call WordApp.Documents.Open("C:\\"+sFileName) If Not doc.Status(0) = "审核完毕" Then Call doc.RemoveItem("Body") End If Else If (doc.Status(0)="草稿") Or (doc.Status(0)="") Then Call WordApp.Documents.Add Else Msgbox "没有找到正文,请先起草正文" OpenWordDoc=False Exit Function End If End If End Function |
这个函数比较简单,首先判断是否已经存在 Word 正文了,如果有了就解压到硬盘然后调用 Open() 方法用 Word 打开该 Word 文档,否则根据当前发文状态,如果在草稿阶段就新建一个 Word 文档,否则报错并退出。
起草正文可以多人多次来完成,一旦最终完成就可以点击“提交审核”操作进入审核阶段,该操作代码如下,只是将 Status 域的值设为“审核中”,由于代码很简单,这里就不分析了。
清单 3
Sub Click(Source As Button) Dim ws As New NotesUIWorkSpace Dim uidoc As NotesUIDocument Dim doc As NotesDocument Set uidoc = ws.CurrentDocument Set doc = uidoc.Document Dim rtitem As NotesRichTextItem Set rtitem = doc.GetFirstItem("Body") If rtitem Is Nothing Then Msgbox "没有找到正文,请先起草正文" Exit Sub End If Dim sFileName As String sFileName = Trim(uidoc.FieldGetText("Title"))+".doc" Dim attachment As NotesEmbeddedObject Set attachment = doc.GetAttachment(sFileName) If attachment Is Nothing Then Msgbox "没有找到正文,请先起草正文" Exit Sub End If uidoc.EditMode = True Call uidoc.FieldSetText("Status", "审核中") Call uidoc.Save Msgbox "成功提交审核" Call uidoc.Close End Sub |
在提交审核后,就进入审核阶段,点击“审核正文”操作将会打开 Body 域中的 Word 正文并设置正文为修订状态,这样所有对文档的改动都将被记录下来。代码如下:
清单 4
Sub Click(Source As Button) Dim activedoc As Variant Dim WordApp As Variant Dim s As New NotesSession Dim ws As New NotesUIWorkspace Dim db As NotesDatabase Dim uidoc As NotesUIDocument Dim doc As NotesDocument Set db = s.CurrentDatabase Set uidoc = ws.CurrentDocument Set doc = uidoc.Document Dim rtitem As NotesRichTextItem Set rtitem = doc.GetFirstItem("Body") If rtitem Is Nothing Then Msgbox "没有找到正文,请先起草正文" Exit Sub End If '定义文件名和全路径文件名 Dim sFileName As String, sFilePath As String '根据标题来确定正文文件名 sFileName = Trim(uidoc.FieldGetText("Title"))+".doc" sFilePath = "C:\\"+sFileName '创建 Word 对象 Set WordApp=CreateObject("Word.Application") WordApp.Visible=True '隐藏 Word 中相关菜单 Call HideWordMenu(WordApp) '用 Word 打开正文 If Not OpenWordDoc(WordApp, uidoc, sFileName) Then Exit Sub '激活当前的文档 Set activedoc = WordApp.ActiveDocument '设置 Word 文档的作者为当前用户 WordApp.Username = s.CommonUsername WordApp.Activate activedoc.Activate '最大化 Word 窗口 WordApp.WindowState =1 '设置痕迹保护 If activedoc.ProtectionType = -1 Then activedoc.Protect(0) End If activedoc.TrackRevisions = True activedoc.PrintRevisions = True activedoc.ShowRevisions = True '审核完成后保存并退出 Word Msgbox "请单击确定编辑完成",32,"消息" '恢复 Word 菜单 Call WordApp.CommandBars("Menu Bar").Reset Call activedoc.SaveAs(sFilePath) Call activedoc.Close(0) Call WordApp.Quit(0) Set activedoc = Nothing Set WordApp = Nothing '附上审核后的正文 Set rtitem = New NotesRichTextItem(doc, "Body") Call rtitem.EmbedObject( EMBED_ATTACHMENT , "" , sFilePath ) doc.SaveOptions = 0 Call uidoc.Close doc.SaveOptions = 1 doc.Status = "审核中" Call doc.Save(True, True) '删除在硬盘的临时文件 Kill sFilePath Call ws.EditDocument(False, doc) Msgbox "正文审核结束" End Sub |
在上面的代码中,需要详细讨论的有两部分。
第一是通过调用过程 HideWordMenu() 来隐藏 Word 中相关菜单,第二是实现具体修订留痕的部分,下面分别分析。
通过调用过程 HideWordMenu() 来隐藏 Word 中相关菜单,代码如下:
清单 5
Sub HideWordMenu(wordapp As Variant) Dim commandBar As Variant '隐藏工具菜单 Set commandBar = WordApp.CommandBars.FindControl(,30007,,True) If Not commandBar Is Nothing Then commandBar.Visible = False commandBar.Enabled = False End If '隐藏 VB 编辑器菜单 Set commandBar = WordApp.CommandBars.FindControl(,1695,,True) If Not commandBar Is Nothing Then commandBar.Visible = False commandBar.Enabled = False End If '隐藏录制新宏菜单 Set commandBar = WordApp.CommandBars.FindControl(,2780,,True) If Not commandBar Is Nothing Then commandBar.Visible = False commandBar.Enabled = False End If End Sub |
这个过程隐藏了 Word 的工具菜单,VB 编辑器菜单和录制新宏菜单,这是为了避免用户审核的时候的误操作改变了当前文档的修订留痕状态。在实际的应用中,这里更为复杂,可能不仅需要隐藏更多的菜单项,还可能会增加一些自定义的菜单,这与项目的具体情况有关。要隐藏任何菜单,最重要的通过 FindControl() 方法得到该菜单,该方法的详细用法可以参考 VBA 帮助。而要使用此方法,最重要的是得到要隐藏菜单的 ID,比如上面代码中要工具菜单的 ID 是 30007,这个 ID 值是不变的,要得到 Word 菜单的 ID,可以用一个简单的程序得到,这个程序在在 WordTest.nsf 中也有,具体在发文表单的操作“取 Word 菜单 ID”中。代码如下:
清单 6
Sub Click(Source As Button) '创建 Word 对象 Dim ws As New NotesUIWorkspace Set WordApp=CreateObject("Word.Application") WordApp.Visible=True tmp$="" For i=1 To 35000 Set ctl = wordapp.CommandBars.FindControl(,i) If Not ctl Is Nothing Then tmp$=tmp$+Cstr(i)+":Caption="+ctl.Caption+Chr(13)+Chr(10) End If Next Call ws.CurrentDocument.fieldsettext("Body",tmp$) Call WordApp.Quit(0) Set WordApp = Nothing End Sub |
只要运行这个程序,则会在发文表单的 Body 域中得到所有的 Word 菜单的 ID 和名称的对应关系。这个程序很简单,就是枚举出所有 ID。注意在 WordTest.nsf 中,Body 域和操作“取 Word 菜单 ID”都是隐藏的,要使用此功能时需要先把隐藏公式去掉。
主要代码如下:
清单 7
If activedoc.ProtectionType = -1 Then activedoc.Protect(0) End If activedoc.TrackRevisions = True activedoc.PrintRevisions = True activedoc.ShowRevisions = True |
这里 ProtectionType 返回当前文档的保护类型,可能的值和说明如下:
- -1 没有保护
- 0 只允许修订和批注
- 1 只允许批注
- 2 不能修订和批注
首先判断当前的文档保护是否已经启用,如果没有则将当前文档保护,级别为可以修订和批注。注意一定要先通过 ProtectionType 来判断当前文档是否已经被保护,否则如果文档已经被保护则 Protect 方法会报错。
然后分别设置 TrackRevisions,PrintRevisions 和 ShowRevisions 三个属性为 true,这就保证了当用户修改文本时所有的修改痕迹都会被标记,并且所有修改痕迹会显示,打印时也会打印出来。这里说的修改痕迹包括增加,修改和删除。下面是这三个属性的说明:
- Document.TrackRevisions: 如果该属性值为 True,则标记对指定文档的修改。Boolean 类型,可读写。
- Document.PrintRevisions: 如果该属性值为 True,则在打印文档的同时打印修订标记。如果返回 False,则不打印修订标记(即打印接受修订后的状态)。Boolean 类型,可读写。
- Document.ShowRevisions: 如果该属性值为 True,则在屏幕上显示对指定文档的修订。Boolean 类型,可读写。
在所有的人都审核完毕后,就可以将正文设为只读的保护起来,同时提供查看正文的功能,查看正文又分为显示修订信息和隐藏修订信息两种,可以让有权限的用户查看,这几个功能主要还是通过设置文档保护和修订状态来实现的,具体代码可以在 WordTest.nsf 的发文表单的相应操作中看到。
下面是 WordTest.nsf 中演示公文留痕的步骤和截图:
- 在 Notes Client 中打开 WordTest.nsf
图 1. 在 Notes Client 中打开 WordTest.nsf
- 在视图中点击“新建”按钮新建一个发文文档。
Figure xxx. Requires a heading
- 在视图中点击“新建”按钮新建一个发文文档。
图 2. 新建 Word 文档
- 输入标题,点击“起草正文”按钮后,将会新建一个 Word 文档,在此文档中起草的发文内容。
图 3. 切换到 Notes 客户端
- 点击确定后,系统将自动保存 Word 文档并以附件形式保存在隐藏的 Body 域中。
- 起草可以多次,再次点击“起草正文”按钮可以继续在原来的草稿中编辑。
- 起草完成后,点击“提交审核”按钮,这样正文的状态被置为“审核中”,同时,隐藏“起草正文”和“提交审核”按钮,显示“审核正文”和“审核完毕”按钮。
图 4. 提交审核显示“审核正文”和“审核完毕”按钮
- 点击“审核正文”按钮进入审核过程,这个过程可以由多人完成,在此过程中,任何人对正文的修改痕迹都会被保留,包括被谁修改的也会保留,同时可以看到相应的菜单已经被隐藏。
图 5. 进入审核过程
- 审核完成后,点击“审核完毕”按钮,则正文将被置于只读状态,表示这是最终修订过的版本了。同时,原来的“审核正文”和“审核完毕”按钮被隐藏,显示“查看正文”按钮。
图 6. 查看正文
- 查看正文时,如果选择显示修订信息的方式,则内容和最后审核完毕前的状态相同,都是直接将修改痕迹显示出来,只是不能再次修改了,如果选择隐藏修订信息的方式,则所有的修改痕迹会被合并然后显示。
图 7. 查看正文
本文通过这个示例详细展示了如何利用 LotusScript 来实现 OA 系统中常用的公文留痕功能,希望能够抛砖引玉,以此为基础,从而实现更为复杂的留痕功能。
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
本文代码下载 | WordTest.zip | 41 KB | HTTP |
学习
- 参阅 developerWorks 文章:“如何在 LotusScript 中处理组”。
- 参阅 developerWorks 文章:“用 LotusScript 实现 Excel 报表的自动生成和操作”。
- 参阅 developerWorks 文章:“在 LotusScript 中为自定义对象模拟事件”。
- 参阅 developerWorks 文章:“利用 LotusScript 灵活操作 Lotus Notes 富文本域”。
讨论