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,这个能够找到对应窗口的引用,有了这个,还不会控制吗?!

 

 

posted @ 2012-07-19 15:47  weep  阅读(170)  评论(0)    收藏  举报