FCKeditor的几点重要改进和使用心得[转载]
以前公司购买过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 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> 3 <web-app> 4 <display-name>FCKeditor Test Application</display-name> 5 <context-param> 6 <!-- setting the FCKecitor context based parameters --> 7 <!-- baseDir means the root of the uploaded file/image/flash stored the 8 prefix of 'file:/' means strore in a file system root that cannot get from 9 webapp url --> 10 <param-name>baseDir</param-name> 11 <param-value>file:/C:/Temp/FCKeditorUpload/</param-value> 12 </context-param> 13 14 <context-param> 15 <!-- if the baseDir prefix by 'file:/',please set it. --> 16 <param-name>fileLocator</param-name> 17 <param-value>/editor/filemanager/browser/default/service/jsp/filelocator</param-value> 18 </context-param> 19 20 <context-param> 21 <!-- debug setting,true means verbose output to the console. --> 22 <param-name>debug</param-name> 23 <param-value>true</param-value> 24 </context-param> 25 26 <context-param> 27 <!-- enabled setting,true means upload enabled. --> 28 <param-name>enabled</param-name> 29 <param-value>true</param-value> 30 </context-param> 31 32 <context-param> 33 <!-- encoding,the response encoding of the file/image/flash,default is 34 UTF-8 --> 35 <param-name>encoding</param-name> 36 <param-value>UTF-8</param-value> 37 </context-param> 38 39 <context-param> 40 <!-- contentTypeMapping,a map for the response ContentType --> 41 <param-name>contentTypeMapping</param-name> 42 <param-value>doc=application/vnd.ms-word 43 |xls=application/vnd.ms-excel 44 |jpg=image/jpeg 45 |gif=image/gif 46 |swf=application/x-shockwave-flash 47 |avi=video/x-msvideo 48 </param-value> 49 </context-param> 50 51 <context-param> 52 <!-- allowedExtensionsFile,the logic is 'Not allowed means deny.' --> 53 <param-name>allowedExtensionsFile</param-name> 54 <param-value>doc|xls|pdf|avi</param-value> 55 </context-param> 56 57 <context-param> 58 <!-- allowedExtensionsImage,the logic is 'Not allowed means deny.' --> 59 <param-name>allowedExtensionsImage</param-name> 60 <param-value>jpg|gif|png</param-value> 61 </context-param> 62 63 <context-param> 64 <!-- allowedExtensionsFlash,the logic is 'Not allowed means deny.' --> 65 <param-name>allowedExtensionsFlash</param-name> 66 <param-value>swf|fla</param-value> 67 </context-param> 68 69 <servlet> 70 <servlet-name>Connector</servlet-name> 71 <servlet-class>com.fredck.FCKeditor.connector.ConnectorServlet</servlet-class> 72 <load-on-startup>1</load-on-startup> 73 </servlet> 74 75 <servlet> 76 <servlet-name>FileLocator</servlet-name> 77 <servlet-class>com.fredck.FCKeditor.service.FileLocatorServlet</servlet-class> 78 <load-on-startup>1</load-on-startup> 79 </servlet> 80 81 <servlet> 82 <servlet-name>SimpleUploader</servlet-name> 83 <servlet-class>com.fredck.FCKeditor.uploader.SimpleUploaderServlet</servlet-class> 84 <load-on-startup>1</load-on-startup> 85 </servlet> 86 87 <servlet-mapping> 88 <servlet-name>Connector</servlet-name> 89 <url-pattern>/editor/filemanager/browser/default/connectors/jsp/connector</url-pattern> 90 </servlet-mapping> 91 92 <servlet-mapping> 93 <servlet-name>SimpleUploader</servlet-name> 94 <url-pattern>/editor/filemanager/upload/simpleuploader</url-pattern> 95 </servlet-mapping> 96 97 <servlet-mapping> 98 <servlet-name>FileLocator</servlet-name> 99 <url-pattern>/editor/filemanager/browser/default/service/jsp/filelocator</url-pattern> 100 </servlet-mapping> 101 </web-app>
连带FCKeditorConfigurations.java一并修理,配置统一且singleton。关键代码为:
/** * Make the configuration sigleton * @param sc * @return the static configuration map */ public static Map getContextConfigurationsInstance(ServletContext sc){ if(contextConfigurations == null){ initContextConfigurations(sc); } return contextConfigurations; } /** * Init all the FCKeditor configuration. * add by zhengxq * @param sc */ private static void initContextConfigurations(ServletContext sc){ if(debug){ System.out.println("\r\n---- FCKeditorConfigurations for java initialization started ----"); } String baseDir = sc.getInitParameter("baseDir"); String fileLocator = sc.getInitParameter("fileLocator"); String debugStr = sc.getInitParameter("debug"); String enabledStr = sc.getInitParameter("enabled"); String encoding = sc.getInitParameter("encoding"); String contentTypeMapping = sc.getInitParameter("contentTypeMapping"); String AllowedExtensionsFile = sc.getInitParameter("allowedExtensionsFile"); String AllowedExtensionsImage = sc.getInitParameter("allowedExtensionsImage"); String AllowedExtensionsFlash = sc.getInitParameter("allowedExtensionsFlash"); debug = ( new Boolean(debugStr)).booleanValue(); encoding = (encoding == null || encoding.length() == 0 ) ?"UTF-8":encoding; if (baseDir == null || baseDir.length() == 0 ) baseDir = defaultBaseDir; String realBaseDir = defaultBaseDir; if (baseDir.startsWith(fileSystemUriPrefix)) { realBaseDir = baseDir.substring(fileSystemUriPrefix.length()); }else{ realBaseDir = sc.getRealPath(baseDir); fileLocator = null ; // no use and should set null } File baseFile = new File(realBaseDir); if ( ! baseFile.exists()) { baseFile.mkdir(); } contextConfigurations = new HashMap(); contextConfigurations.put("baseDir",baseDir); contextConfigurations.put("realBaseDir",realBaseDir); contextConfigurations.put("fileLocator",fileLocator); contextConfigurations.put("debug",debugStr); contextConfigurations.put("enabled",enabledStr); contextConfigurations.put("encoding",encoding); contextConfigurations.put("contentTypeMapping",contentTypeMappingToMap(contentTypeMapping)); contextConfigurations.put("allowedExtensionsFile",stringToArrayList(AllowedExtensionsFile)); contextConfigurations.put("allowedExtensionsImage",stringToArrayList(AllowedExtensionsImage)); contextConfigurations.put("allowedExtensionsFlash",stringToArrayList(AllowedExtensionsFlash)); if (debug){ System.out.println("\r\n---- FCKeditorConfigurations for java initialization end ----"); } }
FileLocatorServlet.java也很简单,无非就是文件的物理定位和文件流的输出:
String type = request.getParameter( " Type " ); String fileName = request.getParameter( " FileName " ); String realFilePath = config.get( " realBaseDir " ) + type + " / " + fileName; File file = new File(realFilePath); if (file.exists()) { response.setHeader( " Content-Transfer-Encoding " , " base64 " ); response.setHeader( " Cache-Control " , " no-store " ); response.setHeader( " Pragma " , " no-cache " ); response.setDateHeader( " Expires " , 0 ); response.setContentType(getContentTypeByFileExt(fileName.substring(fileName.lastIndexOf( " . " )))); ServletOutputStream out = response.getOutputStream(); InputStream in = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(in); BufferedOutputStream bos = new BufferedOutputStream(out); byte [] buff = new byte [ 2048 ]; int bytesRead; while ( - 1 != (bytesRead = bis.read(buff, 0 , buff.length))) { bos.write(buff, 0 , bytesRead); } if (bis != null ) { bis.close(); } if (bos != null ) { bos.close(); } } else { throw new FileNotFoundException(fileName); }
上述改动已经提交给了FCKeditor,如果大家真的有兴趣,可以去找里面我所提交的patch。
二、至于上述的2、3问题,同样,动手即可解决,在此略过。
过程中倒是碰到几个有意思的问题,成了花絮,其实也是使用FCKeditor的一些心得,写写可能还有点意思:
1、如何取得FCKeditor的值?
答案:这是我们常常干的事情:取得这个值并赋值给某个hidden,再合法性检查+submit等。怎么取得呢?这样:
var oEditor = FCKeditorAPI.GetInstance('editor') ;
// Get the editor contents in XHTML.
// alert( oEditor.GetXHTML(true) ) ; // "true" means you want it formatted.
document.all( " tip.c_content " ).value = oEditor.GetXHTML( true );
2、如何使得FCKeditor接收tab键?
答案:我们希望界面元素按照外面的安排进行tab切换,但FCKeditor怎么能做到呢?也有办法:
function focusIframeOnTab(caller, tabTargetId, callEvent) { // If keypress TAB and not SHIFT+TAB if (callEvent.keyCode == 9 && ! callEvent.shiftKey){ document.getElementById(tabTargetId).contentWindow.focus();
} }
光光有个函数顶个什么用,还要这样:在之前的那个界面元素中加上下面的事件,如使用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,这个能够找到对应窗口的引用,有了这个,还不会控制吗?!

浙公网安备 33010602011771号