IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Lotus | Web development | XML  >

基于 Lotus Connections 的 Mashup 应用

实现 Communities 中邮件群发功能

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

样例代码


级别: 中级

魏 成利 (weicl@cn.ibm.com), 软件工程师, IBM 中国软件开发实验室 Lotus 测试开发中心
陈 名 (mingchen@cn.ibm.com), 软件工程师, IBM 中国软件开发实验室 Lotus 测试开发中心

2007 年 8 月 31 日

本文对 Web 2.0 中涌现的一些新技术,包括 Ajax、Atom API、Mashup、GreaseMonkey 等做了简要的介绍,并将其应用到 IBM 的新产品 Lotus Connections 中,实现了在 Communities 模块中向社区中全体成员群发邮件的功能,改善了用户体验。

引言

Lotus Connections 是 IBM 基于 Web 2.0 技术推出的一款社会软件(Social Software),旨在加强人们之间的协作能力。它包含 5 个既可以单独使用又可以联合使用的模块,分别是:

请访问 Ajax 技术资源中心,这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。

RSS 订阅 Ajax 相关文章和教程的 RSS 提要

  • Activities:用于对过程的管理、组织与重用;
  • Blogs:博客系统;
  • Communities:基于 Web 的网络社区,您可以和您有共同兴趣的人分享资源;
  • Dogear:社会书签;
  • Profiles:企业人员目录;

本文的目的在于,使用 Web 2.0 的相关技术对 IBM Lotus Connections 产品的各个模块中不完善、不方便之处进行改进,提高 IBM Lotus Connections 产品的可用性。





回页首


问题的提出

IBM Lotus Connections 产品中的 Communities 模块是为有共同兴趣的人群提供的一个网络社区,人们在这里可以共享资源、加入到自己感兴趣的社区中、结识新朋友、扩大个人的社交圈。

在 Communities 当前的版本中,提供了一个叫做 Members 的面板,以分页的形式列出了加入当前社区的人员列表。当鼠标划过某个成员的名字时,将会出现其 Business Card 的链接,点击便可查看该成员的相关信息,包括姓名、工作单位、地址、电话、Email 等信息。


图 1. Members 面板
图 1. Members 面板

当打开某个人的 Business Card 后,我们可以点击他的 Email 地址链接,并调用操作系统默认的邮件程序客户端向其发送邮件。但是,很多时候我们却是想给整个社区的所有成员群发邮件,这就是一件非常麻烦的事情,您需要逐个打开每个人的 Business Card,拷贝他的 Email 到邮件程序客户端的地址栏中。如果该社区中成员过多(比如有 150 多人),那么拷贝每个人的 Email 地址将是一项“不可能完成的任务(mission impossible)”。

脚注:目前 Communties 中没有提供群发邮件的功能,而这个功能是用户在使用过程中很必要的,确实能够给用户的使用带来方便。





回页首


解决方案

那么有什么好的办法来解决这个问题呢?别忘了 Web 2.0 时代可是一个充满激情、创造奇迹的年代,还有什么做不到的呢?

庆幸的是,我们现在有了 Ajax、Atom API、Mashup、GreaseMonkey、Trixie 等一系列激动人心的技术。

Ajax 是由几种蓬勃发展的技术以新的强大方式组合而成。Ajax 包含:

  • 基于 XHTML 和 CSS 标准的表示;
  • 使用 Document Object Model 进行动态显示和交互;
  • 使用 XMLHttpRequest 与服务器进行异步通信;
  • 使用 Javascript 绑定一切;

Ajax 使浏览器可以为用户提供更为自然的浏览体验。在 Ajax 之前,Web 站点强制用户进入提交/等待/重新显示范例,用户的动作总是与服务器的“思考时间”同步。Ajax 提供与服务器异步通信的能力,从而使用户从请求/响应的循环中解脱出来。借助于 Ajax,可以在用户单击按钮时,使用 JavaScript 和 DHTML 立即更新 UI,并向服务器发出异步请求,以执行更新或查询数据库。当请求返回时,就可以使用 JavaScript 和 CSS 来相应地更新 UI,而不是刷新整个页面。最重要的是,用户甚至不知道浏览器正在与服务器通信:Web 站点看起来是即时响应的(更多 Ajax 信息,请参阅 参考资源)。

Atom API 是一种用来发布和修改 Web 资源的应用层协议,它使用 HTTP 协议的方法来操作 Web 资源(更多 Atom 信息,请参阅 参考资源):

  • GET 方法用来获取与查询 Web 资源;
  • PUT 方法用来更新资源;
  • POST 方法用来创建资源;
  • DELETE 方法用来删除资源;

Mashup 是一个 Web 应用程序,它集成了来自多个源的内容并将其交付到一个页面中进行显示。服务器向每个内容源发出请求,解析收到的信息,并将结果综合到一个页面中发给浏览器(更多 Mashup 信息,请参阅 参考资源)。

Mashup 是一个 Web 2.0 站点的数据与另一个站点数据的综合。这种新风格举足轻重,因为它们展示了这类站点是如何将灵活性赋予网站用户及网站所有者的,而且它们也消除了引入那些立足于成型的想法之上的新思路的障碍。mashup 是一种综合了多种资源的 Web 站点。有时,这些资源的数据类别不同;mashup 可以将来自一个站点的当前的运动比赛分数叠加在来自与之相匹配的 Web 服务的运动事件的地图之上。Web 2.0 站点的特点之一就是它们试图充当类似美联社或路透社的角色,旨在为其他网站或信息服务商提供信息来源(参阅 参考资源)。

GreaseMonkey 是新兴的 FireFox 上的一个插件,通过它用户可以改变网页的源码,修改网页的内容。IE 下相对于 GreaseMonkey 的插件是 Trixie。更多关于 GreaseMonkey 和 Trixie 的信息,参阅 参考资源

接下来,我们就使用上述技术来实现 Communities 中群发邮件的功能。(本文以 IBM 的 TAP Server 上的 Communities 为例,所有代码在 Windows XP、FireFox 2.0.0.5 环境下测试通过。)

  1. 首先要安装 GreaseMonkey 插件,具体步骤请参考 Greasemonkey 官方网站(参阅 参考资源)。

  2. 接下来让我们分析一下 Communities 中 Members 面板的源代码。打开 Members 页,在 FireFox 菜单中选择“View”-> “Page Source”, 可以看到该页的源代码:



    图 2. 源代码
    图 2. 源代码

    从上图中可以得知,每个成员的 email 信息都存储在 <span class="email" style="display: none;">email</span> 这样的元素(element)中。我们可以使用 DOM 模型的 Xquery 查询语句来查询这样的元素。

  3. 了解了该页面的结构,我们就可以使用 GreaseMonkey 来修改这个页面。我们先在这个页面上添加一个 Button:

    var oDiv = document.createElement('div')
    oDiv.style.position = "absolute"
    oDiv.style.right = "455px"
    oDiv.style.top = "170px"
    oDiv.style.border = "1px solid #ffffff"
    oDiv.style.width = "100px"
    oDiv.style.height = "30px"
    oDiv.style.backgroundColor = "#ffffff"
    
    var oSendButton = document.createElement('input')
    oSendButton.type = "button";
    oSendButton.name = "send";
    oSendButton.value = "send mail to all";
    oSendButton.style.marginBottom = "8px";
    oSendButton.addEventListener("click", function() {
        sendMails() 
    	}, false)
    
    oDiv.appendChild(oSendButton)
    document.body.appendChild(oDiv)
    

    添加 Button 后的页面如下图所示:



    图 3. 添加 Button 后的页面
    图 3. 添加 Button 后的页面

    接下来让我们实现函数 sendMails():

    var emails=[]
    var tmpString = “”
    function sendMails() {
        allEmailSpan = document.evaluate("//span[@class='email']", document, null, 
            XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null)
    
        for(var i=0;i < allEmailSpan.snapshotLength;i++) {
            current = allEmailSpan.snapshotItem(i)
            emails[k++] = current.childNodes[0].nodeValue
        }
    
        var i = allEmailSpan.snapshotLength
    
        for(var i=0;i<k;i++) {
            tmpString += emails[i] //多个邮件地址
            tmpString += ";" //以;分隔
        }
    
        window.location.href = "mailto:" + tmpString
    }
    

    在以上的实现中,我使用 Xquery 查询来列出所有名称为 span、包含 class 属性且该属性值为“emaild”的页面元素,即当前页面所有成员的 email 字段。把它们组合成群发地址,使用 mailto 来调用默认的邮件客户端程序来发送邮件。

    这种做法存在一个缺陷,就是该方法只能解析出显示在当前页面的成员 email。如果该 Communities 包含了很多的成员(例如:120 个), 那么 Members 面板将以分页的形式来显示成员,默认情况下是每页显示 50 个成员,120 个成员就会用 3 个页面来显示。通过以上这种方法只能得到当前页,也就是 50 个成员的 email。

    既然如此,这个问题该如何解决呢?这就到了 Atom API 和 Ajax 大显身手的时候了。

  4. 使用 Atom API 和 Ajax 来改进上述实现方法。

    Communities 提供了 Atom API 来通过一个 Community 的 UUID 来查询该 Community 的成员。

    /service/atom/community/members?communityUuid=uuid
    

    以下为可用的 URL 参数:



    参数含义参考值
    role在 Communities 中的角色member, owner
    sortField排序字段Name, created
    asc排序是升序还是降序true, false
    page页码 


    但不幸的是,我们无法在一个请求中得到这个 Community 的所有成员,必须使用 URL 参数 page 来一页一页的取回该 Community 的所有成员。为了保证用户的体验,我们决定使用 Ajax 技术来异步的实现这个相对复杂的操作。

    我们在 Button 的 Click 事件中添加了定时器,按页面的顺序每隔 3 秒钟(这个间隔有待商榷 )发送一个请求,取回一个页面来解析其中的成员 email 并保存下来,直到得到所有成员的 email 为止。



    图 4. 处理流程图
    图 4. 处理流程图

    请求一个分页成员信息的 Atom 调用格式如下:

    http://<hostname>/communities/service/atom/community/members?communityUuid=
        <uuid>&page=[1|2|3……]
    

    返回结果的格式如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <feed xmlns="">
        <title type="text"></title>
        <id>id</id>
        <updated></updated>
        <generator uri="http://www.ibm.com/xmlns/prod/sn" version="1.0"></generator>
        <link href="" rel="self"></link>
        <link href="" rel="" type="application/atomcat+xml; charset=UTF-8"></link>
        <link href="" rel="" type="application/atomserv+xml; charset=UTF-8"></link>
        <entry>
            <id>id string contain email</id>
            <title type="text"></title>
            <summary type="text"></summary>
            <link href="" type="application/atom+xml; charset=UTF-8"></link>
            <link href="" type="text/directory"></link>
            <published></published>
            <updated></updated>
            <contributor>
                <email></email>
                <name></name>
            </contributor>
            <link href="" rel="edit"></link>
            <snx:role component="">owner</snx:role>
            <content type="application/xml">
                <div xmlns="http://www.w3.org/1999/xhtml">
                    <span>
                        <a href="" class="url fn"></a>
                        <div>
                            <a href="" class="email"></a>
                        </div>
                        <div class=" x-guid"></div>
                        <div class=" x-community-role">owner</div>
                    </span>
                </div>
            </content>
        </entry>
        <entry>
            ......
        </entry>
        <entry>
            ......
        </entry>
    </feed>
    

    每一个 <entry> 元素对应一个成员的信息。其中,<entry> 的子元素 <email> 中包含了该成员的 email 信息。我们通过 DOM 对象得到 <email> 元素的集合,从中得到成员的 email。

    var strEmails = ""
    var xmlDocument = http.responseXML
    if(xmlDocument != "") {
        var strNodeValue = ""
        var children = xmlDocument.getElementsByTagName("email")
        var cnt = children.length
    
        for(var i=0;i<cnt;i++) {
            strNodeValue = children[i].firstChild.nodeValue
            strEmails += strNodeValue //多个邮件地址
            strEmails += ";" //以分号分隔
        }
    
        if(cnt == 0) { //已经取得了全部成员的信息
            clearInterval(oTimeInterval) //取消定时器
            intPage = 1//复位
            window.location.href = "mailto:" + strEmails //群发邮件
        }
    }
    

    对于循环的实现,我用的是 setInterval() 方法,该方法的作用是隔一定时间调用一次给定的方法或函数,它需要 2 个参数,一个是要调用的方法或函数;另一个是以毫秒计算的时间间隔。我在 Button 的 click 事件中添加 setInterval() 方法。

    var intInterval = 3000 //3000msec
    var oTimeInterval
    oSendButton.addEventListener("click", function() {
        sendRequest()
        /*对于 setInterval() 方法,其需要等待 intInteral 
    	时间之后才第一次调用 sendRequest 方法。这个
    	时间是完全没有必要等待的,故我们可以先调用
    	一次 sendRequest() 方法。*/
        oTimeInterval = setInterval(sendRequest, intInterval)
    	}, false)
    

    sendRequest() 方法的实现如下:

    var cstrUrl = "/communities/service/atom/community/members?communityUuid="
    var intPage = 1
    var strUudi = ""
    var http = getHTTPObject()	//XMLHttpRequest对象
    function sendRequest() {
        var strUrl = cstrUrl + strUuid + "&page=" + intPage
    	
        http.open("GET", strUrl, true)
        http.onreadystatechange = handleHttpResponse
        http.send(null)
    
        intPage = intPage + 1
    
        return
    }
    

    当前 Community 的 UUID 可以通过页面的 location 对象获得:

    var cstrUuid = "communityUuid="
    var intIdx = 0
    var strHref = window.location.href
    
    intIdx =  strHref.indexOf(cstrUuid)
    strUuid = strHref.substring(intIdx + cstrUuid.length, strHref.length)
    

到此,我们已经实现了在 Communities 中群发邮件的功能,不过还存在着很多局限性和不足之处。





回页首


需要改进的地方

我在脚本中使用了 mailto 方法来实现群发邮件,该方法存在着局限性。使用 mailto 方法时,对于它的参数长度是有限制的,一般情况下,如果 mailto 的参数长度超过 255 个字符,该方法将失效。

鉴于此,我又在页面上增加一个文本框(TextArea),在该文本框中显示群发邮件的地址字符串,在其长度超过最大限制时,可以手工拷贝文本框中的群发地址到邮件客户端的地址栏。这并不是一个完美的解决方案,只是权宜之计,今后我会对此部分进行更加深入的研究,以期能够提供一个合理的解决方法。


图 5. 添加的文本框
图 5. 添加的文本框




回页首


总结

Web 2.0 时代确实是个令人激情澎湃的时代,您可以充分发挥您的想象力来改变生活、改变世界!让生活变得更轻松惬意、让世界更加绚丽多彩!

本文的目的只是想起到一个抛砖引玉的作用,使人们尤其是 Web 开发人员能够充分利用 Web 2.0 所带来的革命性成果,在技术的海洋里自由的徜徉。

免责声明和公开声明

本文所述观点是基于作者个人对相关产品的理解,并不代表 IBM 的官方观点,IBM 不对本文中的信息负责。本文是在本人的知识范围内写成的。如果您发现有异议的地方,请与我联系。






回页首


下载

描述名字大小下载方法
本文完整的源代码。sendmails.user.zip2 KBHTTP
关于下载方法的信息


参考资料

学习

讨论


作者简介

魏成利,于 2007 年 4 月加入 IBM,现参与 IBM Lotus Connections 产品的系统测试项目,对于 Web 2.0 及相关技术有浓厚兴趣。您可以通过 weicl@cn.ibm.com 和他联系。


陈名,于 2005 年 12 月加入 IBM,现为 IBM Lotus Connections 产品的系统测试组 Lead,对 Web Service,Web 2.0 及其相关技术有一定了解。目前的兴趣是 Web 2.0,MashUp,RESTful App。您可以通过 mingchen@cn.ibm.com 和他联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款