内容


为 IBM Lotus Domino 应用程序添加提醒功能

使用 Ajax 进行即时消息传递

Comments

从 6.5 版开始,IBM Lotus Notes 就在其应用程序中提供了集成的即时消息传递和到场提醒功能,包括标准的 Notes 邮件模板,并使开发人员能够向定制应用程序添加相同的功能。要是能向 Domino Web 应用程序添加相同的功能该有多好!现在我们将向您展示如何利用 Asynchronous JavaScript and XML(Ajax)实现这一任务。

本文将演示可用于查看 Web 应用程序中有哪些人在线并允许用户间实时交互的技术。为了最大程度地掌握本文介绍的知识,您应具备 IBM Lotus Domino、JavaScript 和 DHTML 方面的知识。示例数据库 Awareness.nsf(请参阅 下载 部分以获取链接)包含了本文所描述的提醒应用程序的可运行示例。

注意:提醒应用程序的基本功能由一些设计对象组成。我们将在本文中描述最重要的对象,还有一些对象未作说明 —— 主要是执行重置状态这类任务的 helper 对象 —— 留给您在闲暇时进行探索。

向应用程序添加在线用户列表

通过在希望显示联系人列表的位置上插入一些 JavaScript 代码,就可以将在线用户列表添加到应用程序的任何页面。图 1 展示了一个在线用户列示。

图 1. 在页面上包含一些简单的 JavaScript 代码
在页面上包含一些简单的 JavaScript 代码
在页面上包含一些简单的 JavaScript 代码

单击图片或用户名,即时消息传递窗口就会出现,如图 2 所示。

图 2. 发起一次即时消息传递对话
发起一次即时消息传递对话

图 3 展示了回复。

图 3. 对传入的即时消息的响应
对传入的即时消息的响应

工作原理

为了维护在线用户列表,您需要 HTML 和 JavaScript,通过它们您可以为每个登录的用户连续运行代理。代理将使用时间戳(记录每个用户最后一次访问的时间)来维护用户文档。如果他或她最后一次访问的时间戳距离现在不超过两分钟,则该用户被定义为在线用户。在视图中维护在线用户列表将便于使用简单的 @DbColumn 公式来显示在线用户。

接下来,您必须能够和在线用户通信。由于已经为每个用户持续运行代理,因此该代理还可以监视是否存在要发送给指定用户的消息。如果存在消息,则该代理将输出 JavaScript 代码来打开包含消息的弹出窗口。

此解决方案中有四个主要设计元素负责完成实际工作。这四个设计元素为:

  • Awareness.js script
  • refreshUserMsg agent
  • Refresh_Logged_in_user_list agent
  • e-msg form

通过 Awareness.js 页面来展示哪些人在线

该页面包含了用来展示 Web 页面中在线用户的 JavaScript 代码。实际的设计如图 4 所示。若要向 Web 页面添加提醒,只需在该页面中包含 JavaScript 引用即可。

注意:在 Awareness 数据库中已包括了一个简单示例。在起始页中可看到包含的示例。

图 4 是从 IBM Lotus Domino Designer 中捕获的内容,它展示了 Awareness.js 页面(请注意第一行代码)。该行代码使用 document.write() 方法将一个较小的隐藏的 iFrame(0*0 像素)放置在 Web 页面上。iFrame 的内容是 refreshUserMsg 代理。当然,并不打算让用户看到 iFrame。iFrame 用于承载 Ajax 调用。

图 4. Lotus Domino Designer 中的 Awareness.js 页面
Lotus Domino Designer 中的 Awareness.js 页面
Lotus Domino Designer 中的 Awareness.js 页面

注意:选中该页面底部的 <Computed Value> 元素来展示用于显示每个名称前带有小照片的用户列表的代码。此功能假定您有一个可用的照片列表。

最后一行代码将查找由代理维护的在线用户并使用 @Implode 函数将列表和必需的 HTML 代码放置在一起,以便在用户单击另一个用户名称或照片时显示消息传递窗口。

通过 refreshUserMsg 代理维护在线用户列表

refreshUserMsg 代理由 Awareness.js 脚本所创建的 iFrame 载入。该代理将完成以下三个任务:

  • 更新用户的在线状态。
  • 检查是否存在要传递的消息,如果存在,则传递这些消息。
  • 设置定时器,用于在一段时间后重新载入代理。(此功能将确保代理保持对新消息的轮询。如果未执行此任务,则仅当用户重新载入或更改 Web 应用程序中的页面时,消息才会传递。)

清单 1 展示了代理的 Initialize 部分。

清单 1. refreshUserMsg 代理的 Initialize 部分
Sub Initialize
	Dim session As New notessession
	Dim doc As notesDocument
	Dim db As NotesDatabase
	Set db = session.CurrentDatabase
	Set doc = session.documentContext
	Print "Content-type:text/html" 
' Format a nice header telling the content type.
	Print ""
	Print |<SCRIPT Language="JavaScript">|
	Print |<!--|
	' Check if user is registered. Set status.
	Call RefreshUser(db, doc)
	' Check for messages.
	Call Check_Messages(db, doc)
	' Check for messages regularly. 
' Specify your polling time here in milliseconds.
	Print |timerID = setTimeout("document.location.reload()",1000);|
	Print |//-->|
	Print |</script>|
End Sub

第一个和第二个 Print 行设置了 Web 浏览器中对象的内容类型。采用这种方式,Lotus Domino 没有插入默认的 HTML 标题,这使请求节省了几个字节。

清单 2 会在每个用户首次访问系统时为其创建一个文档。然后,对于每一个请求,代码将更新用户文档。如果对用户状态文档的修改未超过两分钟,则认为该用户处于在线状态。

请注意对 RefreshUser 的调用(将维持在线用户列表)以及对 Check_Messages 子函数的调用。代理以设置定时器 setTimeout 结束,此定时器将在 1000 毫秒后重新载入 iFrame。

清单 2. RefreshUser 子函数
Sub RefreshUser(db As NotesDataBase, doc As NotesDocument)
	Dim uDoc As NotesDocument
	Dim view As NotesView
	Set view = db.GetView("Users")
	' Find the users status document.
	Set uDoc = view.GetDocumentByKey(doc.Remote_User(0), True)
	If Not uDoc Is Nothing Then ' Set the users status to logged in.
		uDoc.status = "Online"
	Else ' Create a status document for first timers.
		Set uDoc = db.CreateDocument
		uDoc.Form = "user"
		Dim userName As New NotesName(doc.Remote_User(0))
		uDoc.userCN = userName.Common
		uDoc.user   = userName.Canonical
		uDoc.status = "Online"
	End If
	Call uDoc.Save(True, True)
End Sub

如清单 3 所示,代理的最后一个子函数将核对用户名和要传递的任何未处理的 e-msg 表单。如果有相匹配的内容,则代理打开包含消息的弹出窗口。注意 Print 行,此行设置了用于打开消息的 JavaScript 代码。

清单 3. Check_Messages 子函数
Sub Check_Messages(db As NotesDatabase, doc As NotesDocument)
	Dim view As NotesView
	Set view = db.GetView("e-msg (not delivered)") 
' Check for messages.
	Dim mDoc As NotesDocument
	Set mDoc = view.GetDocumentByKey(doc.Remote_User(0), True)
	If Not mDoc Is Nothing then ' User got messages!
		mDoc.delivered = "1" ' Flag as delivered.
		Call mDoc.Save(False, False)
		' Bring up the pop-up.
		doc.thisDb = Evaluate(|@WebDbName|)
		thisDb = doc.thisDb(0)
		Print |iMsg = window.open('/| + thisDb + 
|/e-msg?openForm&to=| + mDoc.fromEncoded(0) + |&unid=| 
+ mDoc.UniversalID + |', 
'','height=220,width=250,resizable=yes,left=500,top=60');|
	End If
End Sub

通过 Refresh_Logged_in_user_list 代理确保用户退出系统

refreshUserMsg 代理将更新每个用户的文档,对于每个到达代理的请求,都会使用最后修改日期来更新文档。只要用户停留在其中一个启用了提醒的页面上 —— 即上述带有 JavaScript 代码的页面,那么该用户的用户文档就会具有一个递增的时间戳。当用户关闭浏览器或离开应用程序后,时间戳将停止工作。您可以利用此行为来判断用户没有在线,通过使用预定的代理来检查时间戳,并在出现更新的时间戳时重置用户状态。

我们将用户状态变为离线的时间间隔设置为两分钟。代理如清单 4 所示。

清单 4. Refresh_Logged_in_user_list 代理
SELECT Form = "user" & status != "not logged in" 
& @Adjust(@Now;0;0;0;0;-2;0) > @Modified;
@Do(
@SetField("status" ; "not logged in")
)

可以在视图中完成此任务,不过您可能知道在视图中违反代码编写规范并使用 @Now 时将面临的问题。将代理排定为按照 Lotus Domino 所允许的时间间隔(五分钟间隔)来运行,这样在某些用户已经停止活动后,其在线状态仍将保持两分钟以上。

通过 e-msg 表单来发送和接收消息

如图 5 所示,e-msg 表单将定义所发送的消息。它既用于开始消息交换,也用于读取和响应。请注意,即使表单用于读取所发送的消息,也总是使用 ?OpenForm URL 命令来打开表单。您可以使用该命令直接从相同的页面创建响应。文档的惟一 ID 将作为参数进行发送,您可以通过使用 @GetDocField 命令方便地获得消息内容。

图 5. e-msg 表单
e-msg 表单
e-msg 表单

首次启动此表单时,将使用 openForm&to=Tomas%20Nielsen 这样的参数。表单的 To 字段将获得该参数,并利用它将消息传递到正确用户。如果需要接收方答复,那么第二次使用该表单时将发送两个参数 to 和 unid。unid 描述了用来存储要显示的消息的文档;然后参数通过使用 @GetDocField 公式来检索消息主体。

如果您的用户启用了弹出窗口拦截器,则应该允许在您自己的域内弹出窗口。否则将无法接收消息。若要避开此类拦截器,您可以使用 DHTML。如果用户在传递消息前关闭了浏览器,则消息将处于等待状态直到用户再次登录系统,并在载入首页时传递消息。

向即时消息添加表情

也许这并不是即时消息传递解决方案中最重要的功能,但您知道,用户需要它。我们已经添加了笑脸图片(smiley face)作为共享资源图片,名为 happy.gif。

回到图 5,看一下 fromMessage 字段。该字段包含以下代码:

清单 5. fromMessage 字段公式
Tmp := @GetDocField( responseUNID; "message");
@ReplaceSubstring(@If(@IsError(Tmp) ; "" ; Tmp) ; 
@NewLine:":-)" ; "<br />":"<img src=\"/"+@WebDbName 
+ "/happy.gif\">")

请注意如何将新行转化为换行符,以及如何将所有 :-) 组合转换为笑脸图片。当然,可以使用更多表情来扩展此功能,不过这些留给您作为娱乐消遣。作为一项新增功能,您还可以使用特定的词作为更好的等价对象。

可以方便地利用此功能进行扩展。例如,可以添加动画 GIF 影片。甚至可以尝试嵌入欢呼人群的声音剪辑,每次登录内部网时该声音将传递给所有在线的同时。(隐含的是,我们的同事不同意这样做。)

性能考虑事项

在 refreshUserMsg 代理中,我们设置了请求到代理之间的时间段。我们使用的值为 1000 毫秒(1 秒)。这个值对于测试或仅有少数用户的情况是很合适的。对于中等规模的公司,我们发现使用 15,000 毫秒的时间段会比较合适。

如果将该技术用于其它目的,如检查用户是否有新的 email 消息,那么安排代理的时间以及调优代理是很重要的,这样代理才不会占用过多的 CPU 和磁盘时间。您可以完全控制进行下一次轮询的时间,因此完全有可能调整服务器负载应增加的值。例如,根据用户在线的数量,可以动态增加两次轮询的时间间隔。

来总结一下性能问题,我们的第一批提醒解决方案可以在 Lotus Domino 5 上正常运行。从此 Lotus Domino 启动代理和处理 HTTP 请求的速度提高了很多。我们可以说,该技术目前非常适合与 Lotus Domino 一起使用。

其他增强功能

为了在一篇文章中涵盖这种即时消息处理技术,我们降低了公司所使用的提醒解决方案版本。例如,我们所使用的版本还包括一个将聊天会话保存到知识库的选项。您可以添加的其他增强功能如下:

  • 将弹出窗口替换为在当前窗口中打开的 DHTML 层。该功能还会解决弹出拦截器问题。
  • 不实时更新在线用户列表。通过使用相同的 DHTML 方法可以添加该功能。
  • 实时发送已认可的任务,从而使用消息传递功能来加速工作流。
  • 用户在线时始终运行代理,这样很易于了解用户的在线时长。我们的站点上有一个页面包含了在线时间最长的用户列表。
  • 允许用户为离线用户编写消息。这样,当用户再次返回并登录系统后,他或她将收到此消息。
  • 使用消息传递功能将消息发送给每个人。我们使用了称为 Public Question 的特性将问题发送给每个人。想答复的那些人可以加入关于特定问题的聊天会话。

结束语

Ajax 向 Web 站点添加了一个全新的维度,它能够使服务器将内容推给您。之所以称为推,是因为在 “拉” 内容的同时,您将其隐藏在幕后。

我们热爱这种技术,原因之一是它不会载入 Microsoft ActiveX 控件、不会实例化 Java Virtual Machine(JVM)实例、也不需要 flash 动画播放器。对于大多数客户机来说,它是一种轻量级、容易的解决方案。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Lotus, Web development, XML
ArticleID=188156
ArticleTitle=为 IBM Lotus Domino 应用程序添加提醒功能
publish-date=01112007