原文:Rich Web Text Editing with Kupu
作者:Robert Jones
原文发表时间:04/28/2005
译者:chenkw
我编写web应用程序也有些年头了,但一直苦于找不到一个称心的方法让用户输入并提交任意文本块。HTML表单的TEXTAREA标记可以处理普通文本,但我的应用程序大多涉及格式更丰富的内容。一个获得富文本(rich text)的办法是,让用户在字处理器中编辑内容然后将文档提交到服务器,但文档提交后便无法随意处理。原来我最需要的编辑器,就是一个可以嵌入到任何网页中的WYSIWG(What You See Is What You Get,所见即所得)编辑器。天哪,我终于找到了我的理想,它就是----Kupu。
Kupu是一个开源的JavaScript应用程序,它实现了一个灵活的、全功能(full-featured)的HTML编辑器,不需要任何插件就可以在网页中运行。它的主要应用是作为内容管理系统(如Zope、Plone)的嵌入式编辑器,提供创建网页的功能。它的设计非常灵活,你几乎可以毫不费力地将其嵌入到任何web应用程序中。
当然,你首先需要明白了它的工作原理。和其他一些优秀的软件一样,Kupu只提供了有限的文档,而且源代码中的注释也少得可怜。我可以理解为什么会这样。开发人员总是将希望有限的时间用于编写新代码,而非撰写文档。然而,这大大降低了其工作对社区的影响。开源软件除了具有可获得性(availability),还应该具有易用性(accessibility)。
我撰写本文,希望提高Kupu的易用性,并演示将它嵌入你的应用程序是一个多么简单的过程。它是一个非常优秀的程序,应该被更多的人接受。
JavaScript
Kupu由Paul Everitt、Guido Wesdorp、Philipp von Weitershausen等人编写,它是JavaScript程序设计的一个精彩示例。用这种语言编一个最简单的应用程序对我来说都是一种考验,Kupu让我惊叹不已。
由于人们意识到XMLHttpRequest的价值,JavaScript目前似乎正处于一个复兴阶段。Google Suggest以及最近给人深刻印象的Google Maps都采用这种技术,Kupu也不例外。XMLHttpRequest是浏览器和服务器之间的一种后台通信方式,这样就不需要在每次数据交换时刷新整个页面。更多内容请参考Drew McLellan的文章Very Dynamic Web Interfaces。此外Joel Webber在其blog里撰文剖析了Google Maps的实现技术。
不幸的是,使用JavaScript,特别是和XMLHttpRequest一起,会让你卷入浏览器兼容性的漩涡。Kupu目前仅在Mozilla 1.3.1、Internet Explorer 5.5和Netscape Navigator 7.1或更高版本上测试通过。它尚不能在Konqueror、Operah和Safari上正确运行,但新版本有望支持这些浏览器。
安装Kupu
基本安装是非常简单的。你只需要到 http://kupu.oscom.org/download/ 下载压缩包(450K),然后解压到网站的一个可访问的目录下就行了。它将创建kupu/目录,其下包含一些文件和子目录。你不需要进行make或者其他类似的操作。但我建议你先不要看doc/子目录下的内容。
在浏览器中输入<your path>/kupu/common/kupu.html(<your path>指kupu所在的路径,你需要补充完整这个URL)就可以看到编辑器界面了。你也可以访问我的Kupu编辑器测试页面。结果如图1所示。
图1 Kupu示例页面
该页面由三部分组成:工具栏和左右面板。顶部的工具栏包含了编辑器标准功能按钮。右侧面板由一些块(block)组成,每个块为相应功能提供额外信息。左侧面板就是你正在编辑的文档。在本示例中,左侧面板预装入了简要介绍Kupu设计理念的文档。
在左侧面板内某处单击然后就可以开始输入。如果你使用Firefox浏览器,而上述操作没有反应,那么请按F7键。按F7可以启动“插入浏览”(caret browsing),修从而复这个问题。这个界面有些怪异,但还是挺直观的。选择一些文本然后改变其颜色和格式。创建一些链接。插入源为某URL的图片并改变其大小。最好再插入一个表格并增删其行列。这个演示程序并不支持保存操作,它将触发一个错误。我将在下文介绍如何修复这个错误。
Kupu目前的版本也许不能满足你对它作为WYSIWYG编辑器的全部期望。例如,你无法改变字体,只能插入Web上的图片。还有一些功能是不正常的,至少在我的机器上如此。但其兼容性对多数应用程序来说已经绰绰有余了。若你不想涉及插件或者Java applet,Kupu绝对是一个让人振奋的成就。它仅仅是一个包含了JavaScript的网页。
Kupu的实际价值在于它可以嵌入到其他应用程序中。嵌入方式有两种。最简单的用法是使用POST方法将数据作为一个CGI脚本参数值发送到服务器。另一个用法稍有点复杂,但它是Kupu小组的选择,那就是使用PUT方法和一个简单的CGI脚本将数据发送到web站点的一个文件中。
使用Kupu
嵌入Kupu利用其功能是以在页面中包含许多JavaScript文件为代价的。即使像common/kupu.html这样的基本页面中也包含了让人望而生畏的代码。更要命的是,包含文件的链接都是相对的,这会让你很头疼,若你想在Kupu源程序结构上花些时间的话。你其实不想碰这些代码,但是你需要建立自定义内容和Kupu之间的关联并让一切正常运行。幸运的是,你可以使用<base>标记,这是一个简单办法。
将Kupu发布版放到你的站点树(web tree)的某子目录下。然后在站点树的其他地方为你的网页建立一个目录。将模板页(kupu/common/kupu.html or kupu/common/kupuform.html)复制过去,并在页面的<head>段中添加<base>标记,指向发布版的common目录。例如:
<head>
<base href="http://www.craic.com/oreilly/kupu/kupu/common/>
</head>
浏览器将在所有相对链接前附加该URL。注意,它必须是一个包括主机名的完整URL路径;这个规则适用于该页面中的所有相对URL,而不仅是Kupu用到的。在插入图片、样式表、或链向本站其他页面时,务必注意<base>标记的影响。
以PUT方法使用Kupu
整合Kupu的第一种方法是使用PUT方法上传文件到web服务器。PUT虽然也是http协议的一种方法,但在知名度上无法与GET和POST相提并论。PUT允许上传完整的文件,然后将其置入站点树中。当然,你也可以使用HTML表单和CGI脚本来完成同样任务,但PUT的目的就是为了简化这种过程。《Publishing Pages with PUT》一文提供了一些背景知识,该文发表于1997年。
也许大家很少使用PUT方法的主要原因就是其固有的不安全性了。PUT方法有允许任何人提交任何文件到服务器的安全隐患。你需要仔细配置服务器,避开任何潜在的安全漏洞。任何一个小小的失误都可能给攻击者创造莫大机会。相反,你可以考虑使用POST方法打造一个封闭、安全的系统,通过CGI脚本与之交互。这将要求更多编程工作,但其风险更易于管理。
一个PUT请求将发送两行头信息,然后才是请求内容。CONTENT_LENGTH告诉服务器发送的数据长度。PATH_TRANSLATED指定数据写入哪个文件。你可以将其视为浏览器从服务器获取一个页面的逆过程。Apache httpd配置默认接受PUT请求,但你还需要配置一个CGI脚本来处理接收的文件。下面是CGI脚本配置示例,你只需要根据你的情况替换掉脚本名就可以了:
Script PUT /cgi-bin/kupu/handle_put.cgi
以下是一个简单的CGI脚本实现。注意,该脚本中没有考虑安全检查。任何人都可以向运行了该脚本的站点上传文件。在公网站点上安装该脚本时务必添加安全限制。切记!(本示例并非我的站点上运行的应用程序)
#!/usr/bin/perl -w
# handle_put.cgi
# Basic PUT Handling routine with NO SECURITY!
if($ENV{'REQUEST_METHOD'} ne 'PUT') {
errorMsg("Request method is not PUT");
}
my $filename = $ENV{'PATH_TRANSLATED'};
if(not $filename) {
errorMsg("PATH_TRANSLATED was empty");
}
my $length = $ENV{'CONTENT_LENGTH'};
if(not $length) {
errorMsg("CONTENT_LENGTH was empty");
}
# Add Security Checks Here! Limit access to certain
# directories and limit size and/or type of file. E.g.
if($length > 100000) {
errorMsg("CONTENT_LENGTH is too large");
}
# Read in the uploaded data
my $content = '';
my $nread = read(STDIN, $content, $length);
# Make the output more readable by adding newlines
$content






