以前公司购买过eWebEditor,功能应该还是不错的,但即便到了现在,也还仅是一个IE only的版本,无法满足现在差异化的需求。故前段时间下了最新的FCKeditor2.3.3版本下来(当然了,连带java的integration),demo来看看,发现有几个地方非常不爽:
1、上载的文件,只能放在URL可及的地方(如默认只能放到嵌入应用路径的/UserFiles/下);
2、没有明确的上载视频的按钮;
3、图片、FLASH、附件上载等,步骤多,复杂度高(想想,用户不都是高手)。
怎么办呢,改!
一、第一个就是增加一个FileLocatorServlet,思路很简单:通过这个服务来定位文件,而不是之间产生链接,既是安全的考虑,也是应用集群的一个重要考虑点。而且原来的几个servlet的配置罗嗦且重叠,难以让人产生美感。所谓代码胜千言,通过下面的web.xml大家应该可以看出修理的要点:
1
<?
xml version="1.0" encoding="ISO-8859-1"
?>
2
3
<!
DOCTYPE web-app
4
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
5
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"
>
6
7
<
web-app
>
8
<
display-name
>
FCKeditor Test Application
</
display-name
>
9
<
context-param
>
10
<!--
setting the FCKecitor context based parameters
-->
11
<!--
baseDir means the root of the uploaded file/image/flash stored
12
the prefix of 'file:/' means strore in a file system root that cannot get from webapp url
13
-->
14
<
param-name
>
baseDir
</
param-name
>
15
<
param-value
>
file:/C:/Temp/FCKeditorUpload/
</
param-value
>
16
</
context-param
>
17
18
<
context-param
>
19
<!--
20
if the baseDir prefix by 'file:/',please set it.
21
-->
22
<
param-name
>
fileLocator
</
param-name
>
23
<
param-value
>
/editor/filemanager/browser/default/service/jsp/filelocator
</
param-value
>
24
</
context-param
>
25
26
<
context-param
>
27
<!--
28
debug setting,true means verbose output to the console.
29
-->
30
<
param-name
>
debug
</
param-name
>
31
<
param-value
>
true
</
param-value
>
32
</
context-param
>
33
34
<
context-param
>
35
<!--
36
enabled setting,true means upload enabled.
37
-->
38
<
param-name
>
enabled
</
param-name
>
39
<
param-value
>
true
</
param-value
>
40
</
context-param
>
41
42
<
context-param
>
43
<!--
44
encoding,the response encoding of the file/image/flash,default is UTF-8
45
-->
46
<
param-name
>
encoding
</
param-name
>
47
<
param-value
>
UTF-8
</
param-value
>
48
</
context-param
>
49
50
<
context-param
>
51
<!--
52
contentTypeMapping,a map for the response ContentType
53
-->
54
<
param-name
>
contentTypeMapping
</
param-name
>
55
<
param-value
>
doc=application/vnd.ms-word
56
|xls=application/vnd.ms-excel
57
|jpg=image/jpeg
58
|gif=image/gif
59
|swf=application/x-shockwave-flash
60
|avi=video/x-msvideo
61
</
param-value
>
62
</
context-param
>
63
64
<
context-param
>
65
<!--
66
allowedExtensionsFile,the logic is 'Not allowed means deny.'
67
-->
68
<
param-name
>
allowedExtensionsFile
</
param-name
>
69
<
param-value
>
doc|xls|pdf|avi
</
param-value
>
70
</
context-param
>
71
72
<
context-param
>
73
<!--
74
allowedExtensionsImage,the logic is 'Not allowed means deny.'
75
-->
76
<
param-name
>
allowedExtensionsImage
</
param-name
>
77
<
param-value
>
jpg|gif|png
</
param-value
>
78
</
context-param
>
79
80
<
context-param
>
81
<!--
82
allowedExtensionsFlash,the logic is 'Not allowed means deny.'
83
-->
84
<
param-name
>
allowedExtensionsFlash
</
param-name
>
85
<
param-value
>
swf|fla
</
param-value
>
86
</
context-param
>
87
88
<
servlet
>
89
<
servlet-name
>
Connector
</
servlet-name
>
90
<
servlet-class
>
com.fredck.FCKeditor.connector.ConnectorServlet
</
servlet-class
>
91
<
load-on-startup
>
1
</
load-on-startup
>
92
</
servlet
>
93
94
<
servlet
>
95
<
servlet-name
>
FileLocator
</
servlet-name
>
96
<
servlet-class
>
com.fredck.FCKeditor.service.FileLocatorServlet
</
servlet-class
>
97
<
load-on-startup
>
1
</
load-on-startup
>
98
</
servlet
>
99
100
<
servlet
>
101
<
servlet-name
>
SimpleUploader
</
servlet-name
>
102
<
servlet-class
>
com.fredck.FCKeditor.uploader.SimpleUploaderServlet
</
servlet-class
>
103
<
load-on-startup
>
1
</
load-on-startup
>
104
</
servlet
>
105
106
<
servlet-mapping
>
107
<
servlet-name
>
Connector
</
servlet-name
>
108
<
url-pattern
>
/editor/filemanager/browser/default/connectors/jsp/connector
</
url-pattern
>
109
</
servlet-mapping
>
110
111
<
servlet-mapping
>
112
<
servlet-name
>
SimpleUploader
</
servlet-name
>
113
<
url-pattern
>
/editor/filemanager/upload/simpleuploader
</
url-pattern
>
114
</
servlet-mapping
>
115
116
<
servlet-mapping
>
117
<
servlet-name
>
FileLocator
</
servlet-name
>
118
<
url-pattern
>
/editor/filemanager/browser/default/service/jsp/filelocator
</
url-pattern
>
119
</
servlet-mapping
>
120
121
</
web-app
>
连带FCKeditorConfigurations.java一并修理,配置统一且singleton。关键代码为:
1
2
/** */
/**
3
* Make the configuration sigleton
4
*
@param
sc
5
*
@return
the static configuration map
6
*/
7
public
static
Map getContextConfigurationsInstance(ServletContext sc)
{
8
if
(contextConfigurations
==
null
)
{
9
initContextConfigurations(sc);
10
}
11
return
contextConfigurations;
12
}
13
14
/** */
/**
15
* Init all the FCKeditor configuration.
16
* add by zhengxq
17
*
@param
sc
18
*/
19
private
static
void
initContextConfigurations(ServletContext sc)
{
20
if
(debug)
21
System.out.println(
"
\r\n---- FCKeditorConfigurations for java initialization started ----
"
);
22
23
String baseDir
=
sc.getInitParameter(
"
baseDir
"
);
24
String fileLocator
=
sc.getInitParameter(
"
fileLocator
"
);
25
String debugStr
=
sc.getInitParameter(
"
debug
"
);
26
String enabledStr
=
sc.getInitParameter(
"
enabled
"
);
27
String encoding
=
sc.getInitParameter(
"
encoding
"
);
28
String contentTypeMapping
=
sc.getInitParameter(
"
contentTypeMapping
"
);
29
String AllowedExtensionsFile
=
sc.getInitParameter(
"
allowedExtensionsFile
"
);
30
String AllowedExtensionsImage
=
sc.getInitParameter(
"
allowedExtensionsImage
"
);
31
String AllowedExtensionsFlash
=
sc.getInitParameter(
"
allowedExtensionsFlash
"
);
32
33
debug
=
(
new
Boolean(debugStr)).booleanValue();
34
encoding
=
(encoding
==
null
||
encoding.length()
==
0
)
?
"
UTF-8
"
:encoding;
35
36
if
(baseDir
==
null
||
baseDir.length()
==
0
) baseDir
=
defaultBaseDir;
37
String realBaseDir
=
defaultBaseDir;
38
if
(baseDir.startsWith(fileSystemUriPrefix))
{
39
realBaseDir
=
baseDir.substring(fileSystemUriPrefix.length());
40
}
else
{
41
realBaseDir
=
sc.getRealPath(baseDir);
42
fileLocator
=
null
;
//
no use and should set null
43
}
44
File baseFile
=
new
File(realBaseDir);
45
if
(
!
baseFile.exists())
{
46
baseFile.mkdir();
47
}
48
contextConfigurations
=
new
HashMap();
49
contextConfigurations.put(
"
baseDir
"
,baseDir);
50
contextConfigurations.put(
"
realBaseDir
"
,realBaseDir);
51
contextConfigurations.put(
"
fileLocator
"
,fileLocator);
52
contextConfigurations.put(
"
debug
"
,debugStr);
53
contextConfigurations.put(
"
enabled
"
,enabledStr);
54
contextConfigurations.put(
"
encoding
"
,encoding);
55
contextConfigurations.put(
"
contentTypeMapping
"
,contentTypeMappingToMap(contentTypeMapping));
56
contextConfigurations.put(
"
allowedExtensionsFile
"
,stringToArrayList(AllowedExtensionsFile));
57
contextConfigurations.put(
"
allowedExtensionsImage
"
,stringToArrayList(AllowedExtensionsImage));
58
contextConfigurations.put(
"
allowedExtensionsFlash
"
,stringToArrayList(AllowedExtensionsFlash));
59
60
if
(debug)
61
System.out.println(
"
\r\n---- FCKeditorConfigurations for java initialization end ----
"
);
62
63
}
FileLocatorServlet.java也很简单,无非就是文件的物理定位和文件流的输出:
1
String type
=
request.getParameter(
"
Type
"
);
2
String fileName
=
request.getParameter(
"
FileName
"
);
3
4
String realFilePath
=
config.get(
"
realBaseDir
"
)
+
type
+
"
/
"
+
fileName;
5
File file
=
new
File(realFilePath);
6
if
(file.exists())
{
7
response.setHeader(
"
Content-Transfer-Encoding
"
,
"
base64
"
);
8
response.setHeader(
"
Cache-Control
"
,
"
no-store
"
);
9
response.setHeader(
"
Pragma
"
,
"
no-cache
"
);
10
response.setDateHeader(
"
Expires
"
,
0
);
11
response.setContentType(getContentTypeByFileExt(fileName.substring(fileName.lastIndexOf(
"
.
"
))));
12
13
ServletOutputStream out
=
response.getOutputStream();
14
InputStream in
=
new
FileInputStream(file);
15
BufferedInputStream bis
=
new
BufferedInputStream(in);
16
BufferedOutputStream bos
=
new
BufferedOutputStream(out);
17
byte
[] buff
=
new
byte
[
2048
];
18
int
bytesRead;
19
while
(
-
1
!=
(bytesRead
=
bis.read(buff,
0
, buff.length)))
{
20
bos.write(buff,
0
, bytesRead);
21
}
22
if
(bis
!=
null
)
{
23
bis.close();
24
}
25
if
(bos
!=
null
)
{
26
bos.close();
27
}
28
}
else
{
29
throw
new
FileNotFoundException(fileName);
30
}
上述改动已经提交给了FCKeditor,如果大家真的有兴趣,可以去找里面我所提交的patch。
二、至于上述的2、3问题,同样,动手即可解决,在此略过。
过程中倒是碰到几个有意思的问题,成了花絮,其实也是使用FCKeditor的一些心得,写写可能还有点意思:
1、如何取得FCKeditor的值?
答案:这是我们常常干的事情:取得这个值并赋值给某个hidden,再合法性检查+submit等。怎么取得呢?这样:
1
var
oEditor
=
FCKeditorAPI.GetInstance('editor') ;
2
//
Get the editor contents in XHTML.
3
//
alert( oEditor.GetXHTML(true) ) ; // "true" means you want it formatted.
4
document.all(
"
tip.c_content
"
).value
=
oEditor.GetXHTML(
true
);
2、如何使得FCKeditor接收tab键?
答案:我们希望界面元素按照外面的安排进行tab切换,但FCKeditor怎么能做到呢?也有办法:
1
function
focusIframeOnTab(caller, tabTargetId, callEvent)
{
2
//
If keypress TAB and not SHIFT+TAB
3
if
(callEvent.keyCode
==
9
&&
!
callEvent.shiftKey)
4
document.getElementById(tabTargetId).contentWindow.focus();
5
}
光光有个函数顶个什么用,还要这样:在之前的那个界面元素中加上下面的事件,如使用struts的tag的化,这样就可以了:
<
html:text
property
="tip.c_title"
style
="width:450px"
tabindex
="1"
onkeydown
="focusIframeOnTab(this, 'editor___Frame',event);if(!document.all) return false;"
/>
这点是google了半天最终在FCKeditor的FAQ中找到的,看来以后用开源的软件第一件事情就是看FAQ,错不了!
3、如何希望在FCKeditor加载完毕后做点什么事情?
答案:也很简单,编写自己的FCKeditor_OnComplete函数,如:
function
FCKeditor_OnComplete( editorInstance )
{
window.status
=
editorInstance.Description ;
}
4、如果在图片、FLASH等界面中上载了东西后,希望能告诉自己的表单,怎么做?
答案:这个花了我不少看代码和调试时间!其实这里的关键就是如何获取嵌入FCKeditor的那个window,这样就可以了,在对应的js文件(如editor\dialog\fck_image\fck_image.js)中的ok方法的最后加入:
//
edit by zhengxq
try
{
var
obj
=
window.dialogArguments.Editor.parent.document;
obj.getElementById(
"
tip.c_tip_has_pic
"
).value
=
"
1
"
;
}
catch
(e)
{}
关键就是:window.dialogArguments.Editor.parent.document,这个能够找到对应窗口的引用,有了这个,还不会控制吗?!