一、關於用戶發表文章的功能設計
用戶發表文章的功能,大家見過不少,也用過不少,最簡單的,莫過於提供一個文本框,數據提交後直接寫入數據庫了事,稍複雜一點的最少也要提供一個輸入標題和選擇分類的功能。當然,我們也可以把我們的功能設計得更有特色。在這個示例項目中,我假設開發的是一個以圖文爲中心的網絡社區,我們每一篇文章都需要用戶在它上傳的圖片中選擇一個作爲主題圖片,那麼,在網站首頁的文章列表上,大家看到的將不僅僅只是一個文字的標題,還有主題圖片的縮略圖。
先來看看數據表的結構,創建數據表的SQL語句如下:
`id` int ( 11 ) NOT NULL auto_increment,
`catalogid` int ( 11 ) NOT NULL ,
`subject` varchar ( 60 ) default NULL ,
`content` text ,
`pictures` varchar ( 2000 ) NOT NULL ,
`mainpicture` varchar ( 40 ) NOT NULL ,
`userid` int ( 11 ) NOT NULL ,
`time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP ,
`lastedittime` timestamp NOT NULL default ' 2007-01-01 00:00:00 ' ,
`lastreplytime` timestamp NOT NULL default ' 2007-01-01 00:00:00 ' ,
`visitcount` int ( 11 ) NOT NULL ,
PRIMARY KEY (`id`),
KEY `subject` (`subject`),
KEY `userid` (`userid`),
KEY `time` (`time`),
KEY `lastreplytime` (`lastreplytime`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8 |
其中,catalogid字段爲文章分類,subject字段爲標題,content字段爲正文。比較特殊的是pictures字段和mainpicture字段,pictures保存文章中包含的所有圖片的url,以“|”符號分割,如“001.jpg|002.jpg|003.jpg...”,而mainpicture就是主題圖片的url了。有人會問:“保存主題圖片的url就夠了,爲什麼還要保存所有的圖片url呢?”,這樣設計主要是爲了考慮到用戶有時候會修改文章,重新選擇別的圖片作爲主題圖片,這個時候pictures字段就派上用場了,因爲它可以向用戶提供候選項。
這樣的功能設計應該提供如下的用戶界面,該頁面文件名爲EditPosts.jsp:
在這裏,我們還沒有Web編輯器可用,暫時用一個文本區域代替。
二、初識FCKeditor
在聽說FCKeditor之前,我用過一個在線編輯器eWebEditor,提供ASP/JSP/PHP等好幾個版本,功能是非常的好,文檔也很詳細,但是聽說只支持IE瀏覽器;而FCKeditor在網上大名鼎鼎,是一個受關注非常高的開源項目,並且能夠跨瀏覽器支持。因此我選擇FCKeditor。FCKeditor的最新版本是2.4,大家可以到
http://www.fckeditor.net/download這裏下載,如下圖
下載並解壓縮到fckeditor文件夾,打開該文件夾,我們可以看到如下文件及目錄:
其中_samples目錄下是示例,_testcases目錄下是測試用例,editor目錄下是編輯器的主要文件;此外,從該目錄中的文件不難看出,FCKeditor提供支持asp、php、perl、python等等各種服務器技術的版本,但不支持.net和Java Web。不過不要擔心,FCKeditor與Java Web之間的整合早就有人做好了,稍後我們就會用到。
瞭解瀏覽器技術的人都不難想到,Web編輯器其實應該是客戶端技術,它是通過JavaScript來控制頁面上的元素和通過彈出窗口來模擬對話框而做到的;只有在提交文章或者上傳文件的時候才需要跟服務器端交互。因此,要將該編輯器快速整合到項目中以看到效果,是非常簡單的。
三、使用JavaScript整合FCKeditor
將剛剛解壓得到的fckeditor目錄拷貝到我們的項目中的src/main/webapp目錄下,打開剛纔建立的EditPosts.jsp,加入如下代碼:
2 < script language = " javascript " >
3 window.onload = function () {
4 var oFCKeditor = new FCKeditor( 'myTextArea' ) ;
5 oFCKeditor.BasePath = " fckeditor/ " ;
6 oFCKeditor.ReplaceTextarea();
7 }
</scrip>
在這裏,第一行代碼是引入fckeditor中的fckeditor.js文件,其中定義了FCKeditor類,第四行就是利用該類創建一個編輯器對象,而myTextArea是表單中文本區域的名字,在第六行,通過FCKeditor類的ReplaceTextArea方法,文本區域就被替換成了Web編輯器。刷新頁面,就可以看到效果:
FCKeditor類提供幾個基本屬性,可以讓我們對編輯器進行簡單的控制,它們是:
Width:設置編輯器的寬度,默認爲100%
Height:設置編輯器的高度,默認值爲200
ToolbarSet:設置編輯器的工具條集合,默認值爲"default",稍後會講到怎樣自定義工具條
Value:設置顯示在編輯器中的內容(包含HTML),默認值爲空
BasePath:編輯器的目錄,一定要設置正確,否則編輯器會找不到它需要的文件,在本例中,由於我們直接將fckeditor目錄放到項目的根目錄下,因此設置爲"fckeditor/"
CheckBrowser:設置是否檢測瀏覽器,默認爲true
DisplayErrors:設置是否顯示錯誤信息,默認爲true
此外,FCKeditor類還有一個集合屬性Config[ key ] = value,通過該集合屬性,我們可以進行一個更高級的設置,如設置默認語言、更換皮膚等等。
綜上所述,下面的代碼將重新設置編輯器的高和寬、將工具條設置爲基本工具條,將皮膚設置爲office2003樣式:
<script language="javascript">
window.onload = function(){
var oFCKeditor = new FCKeditor( 'myTextArea' ) ;
oFCKeditor.BasePath = "fckeditor/";
oFCKeditor.Width = "800";
oFCKeditor.Height = "300";
oFCKeditor.ToolbarSet = "Basic";
oFCKeditor.Config["SkinPath"] = "skins/office2003/";
oFCKeditor.ReplaceTextarea();
}
</script>
效果圖:
四、通過FCKeditor.java整合FCKeditor
使用JavaScript整合FCKeditor,我們很快就能看到編輯器的效果,並進行文章的編輯。但是,在需要和服務器端進行交互的時候(比如上傳圖片),就會出錯。因此,我們不得不在服務器端做一點手腳。這裏,我們需要使用的是FCKeditor.java,其最新版本是2.3,還是在剛纔的下載頁面,找到下載鏈接,如下圖:
將下載文件解壓,我們可以看到有doc目錄,有src目錄,甚至還有一個build.xml,讓我們可以重新構建項目;但是,這些我們統統都不需要,我們只要web/WEB-INF目錄下的東西,在這個目錄下,提供了一個web.xml,同時在lib目錄下提供了兩個.jar文件,這便是全部。看到這裏,大家肯定能夠想到,Java Web項目的靈魂是什麼?那就是web.xml。我們所要做的,就是把lib目錄下的兩個.jar文件拷貝到我們項目的src/main/webapp/WEB-INF/lib下,同時將web.xml中的內容整合到我們項目的src/main/webapp/WEB-INF/web.xml中。
web.xml中的內容很簡單,只定義了兩個Servlet映射,並且對上傳文件的目錄和允許哪些文件上傳、拒絕哪些文件上傳做了設置,如下:
<servlet-name>Connector</servlet-name>
<servlet-class>com.fredck.FCKeditor.connector.ConnectorServlet</servlet-class>
<init-param>
<param-name>baseDir</param-name>
<param-value>/UploadFiles/</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>SimpleUploader</servlet-name>
<servlet-class>com.fredck.FCKeditor.uploader.SimpleUploaderServlet</servlet-class>
<init-param>
<param-name>baseDir</param-name>
<param-value>/UploadFiles/</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>enabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>AllowedExtensionsFile</param-name>
<param-value></param-value>
</init-param>
<init-param>
<param-name>DeniedExtensionsFile</param-name>
<param-value>php|php3|php5|phtml|asp|aspx|ascx|jsp|cfm|cfc|pl|bat|exe|dll|reg|cgi</param-value>
</init-param>
<init-param>
<param-name>AllowedExtensionsImage</param-name>
<param-value>jpg|gif|jpeg|png|bmp</param-value>
</init-param>
<init-param>
<param-name>DeniedExtensionsImage</param-name>
<param-value></param-value>
</init-param>
<init-param>
<param-name>AllowedExtensionsFlash</param-name>
<param-value>swf|fla</param-value>
</init-param>
<init-param>
<param-name>DeniedExtensionsFlash</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Connector</servlet-name>
<url-pattern>/fckeditor/editor/filemanager/browser/default/connectors/jsp/connector</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SimpleUploader</servlet-name>
<url-pattern>/fckeditor/editor/filemanager/upload/simpleuploader</url-pattern>
</servlet-mapping>
請注意,這兩個servlet的url-pattern我都在原來代碼的前面加上了/fckeditor。
然後,我們就可以拋開JavaScript,而在服務器端使用標籤來創建Web編輯器了。先在EditPosts.jsp中引入標籤庫:
再在原來放textarea的地方,放如下代碼:
imageBrowserURL="/xkland/fckeditor/editor/filemanager/browser/default/browser.html?Type=Image&Connector=connectors/jsp/connector"
linkBrowserURL="/xkland/fckeditor/editor/filemanager/browser/default/browser.html?Connector=connectors/jsp/connector"
flashBrowserURL="/xkland/fckeditor/editor/filemanager/browser/default/browser.html?Type=Flash&Connector=connectors/jsp/connector"
imageUploadURL="/xkland/fckeditor/editor/filemanager/upload/simpleuploader?Type=Image"
linkUploadURL="/xkland/fckeditor/editor/filemanager/upload/simpleuploader?Type=File"
flashUploadURL="/xkland/fckeditor/editor/filemanager/upload/simpleuploader?Type=Flash">
This is some <strong>sample text</strong>. You are using <a href="http://www.fredck.com/fckeditor/">FCKeditor</a>.
</FCK:editor>
這裏有一點一定要注意,那就是這裏的屬性都要避免使用相對路徑。
刷新頁面,又見編輯器,此時,可以順利的上傳文件了。整合編輯器的任務到此完成。下一步,就是怎樣對編輯器進行更多的控制了。
五、對編輯器進行更多控制
1、自定義工具條:打開fckeditor目錄下的fckconfig.js文件,添加如下代碼:
['Source','Preview'],
['Undo','Redo','-','SelectAll','Cut','Copy','Paste','-','RemoveFormat','-','Find','Replace'],
['Link','Unlink','Anchor'],
['FitWindow','-','About'],
'/',
['Bold','Italic','Underline','StrikeThrough','-','Subscript','Superscript'],
['OrderedList','UnorderedList','-','Outdent','Indent'],
['JustifyLeft','JustifyCenter','JustifyRight','JustifyFull'],
['Image','Flash','Table','Rule','Smiley'],
'/',
['Style','FontFormat','FontName','FontSize'],
['TextColor','BGColor']
] ;
2、添加常用的中文字體:在上面打開的文件中找到
3、更改JSP頁面中定義編輯器的標籤,如下:
skinPath="/xkland/fckeditor/editor/skins/office2003/"
toolbarSet="Usable"
imageBrowserURL="/xkland/fckeditor/editor/filemanager/browser/default/browser.html?Type=Image&Connector=connectors/jsp/connector"
linkBrowserURL="/xkland/fckeditor/editor/filemanager/browser/default/browser.html?Connector=connectors/jsp/connector"
flashBrowserURL="/xkland/fckeditor/editor/filemanager/browser/default/browser.html?Type=Flash&Connector=connectors/jsp/connector"
imageUploadURL="/xkland/fckeditor/editor/filemanager/upload/simpleuploader?Type=Image"
linkUploadURL="/xkland/fckeditor/editor/filemanager/upload/simpleuploader?Type=File"
flashUploadURL="/xkland/fckeditor/editor/filemanager/upload/simpleuploader?Type=Flash">
This is some <strong>sample text</strong>. You are using <a href="http://www.fredck.com/fckeditor/">FCKeditor</a>.
</FCK:editor>
刷新頁面,可以看到編輯器的效果如下:
六、如何獲取編輯器中插入的圖片
從文章開頭的功能設計我們可以看出,當用戶編輯完文章後,我們應該能獲取文章中插入的圖片信息。怎樣獲取編輯器中的插入的圖片呢?IT進行時在他的文章FCKeditor的幾點重要改進和使用心得,值得分享 中是這樣做的:在上傳圖片的對話框的JavaScript中添加代碼,使得當用戶插入圖片點OK後通知列表框,代碼如下:
var obj = window.dialogArguments.Editor.parent.document;
obj.getElementById( " tip.c_tip_has_pic " ).value = " 1 " ;
} catch (e) {}
我認爲這個方法不好,第一,這個方法是侵入性的,需要修改FCKeditor的代碼;第二,這種方法能夠在用戶插入圖片的時候獲得圖片信息,但是如果用戶插入的圖片,接着又把圖片從文章中刪除了呢?這時候是無法跟蹤的。
正確的思路應該是在編輯器失去焦點的時候,獲取編輯器中的文檔,通過DOM取得文章中所有的圖片。代碼如下:
{
editorInstance.Events.AttachEvent( 'OnBlur', onEditorBlur ) ;
}
function onEditorBlur(){
var oSelect = $("img_select");
for(var i=oSelect.options.length-1; i>0; i--){
oSelect.options[i] = null;
}
oEditor = FCKeditorAPI.GetInstance('EditorDefault');
var imgs = oEditor.EditorDocument.body.all.tags("img");
for(var i=0; i < imgs.length; i++){
var oOption = document.createElement("option");
oOption.appendChild(document.createTextNode(imgs[i].src));
oSelect.appendChild(oOption);
}
}
上面是我在探索FCKeditor中的一些心得,有問題的地方,歡迎大家探討。
Feedback
還有對上傳的圖片的管理也是我喜歡的。
對這兩個編輯器,我也要試試看。
CuteEditor、FreeTextBox只支持.NET。
存儲文章類別的表
CREATE TABLE `catalogs` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(20) NOT NULL,
`image` varchar(40) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
存儲文章回復的表
CREATE TABLE `replys` (
`id` int(11) NOT NULL auto_increment,
`subject` varchar(50) NOT NULL,
`content` text,
`pictures` varchar(2000) default NULL,
`userid` int(11) NOT NULL,
`time` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
這裏其實也可以完全不要pictures字段,因爲管理上傳圖片的工作已經完全由FCKeditor編輯器代勞了。
如果要開發一個完善的系統,還有很多問題需要考慮。比如說安全問題。如果有的用戶希望自己上傳的圖片和發表的主題只有自己可以看,那麼我們需要讓用戶選擇是公開發表,還是僅羣內用戶可見,還是隻有自己能看見。因此需要修改topics表,以判斷一篇文章是能夠公開的,還是屬於羣組的,還是隻有自己可以看的。另外,由於一個用戶可以屬於多個組,而有時他並不想一篇文章讓所有的組都可見,所以還需要加入groups_topics表,以幫助某個主題在哪些羣組裏面可以看見。
create table groups_topics(
id int not null auto_increment primary key,
groupid int not null,
topicid int not null,
index(groupid),
index(topicid));
alter table topics
add column ispublic tinyint not null,
add column isgroupvisiable tinyint not null;
此外,由於所有上傳圖片的工作都由編輯器代勞了,所以不需要專門的表來對上傳文件進行維護。
經過我對FCKeditor的文檔的反覆閱讀,發現FCKeditor自帶的API沒有辦法實現這樣的功能,所以,修改的重點還是在FCKeditor.java中。我們可以對源代碼進行如下修改:
1、打開FCKeditor-2.3/src/com/fredck/FCKeditor/uploader目錄下的SimpleUploaderServlet.java文件,找到SimpleUploaderServlet類的doPost方法,它的代碼如下:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (debug) System.out.println("--- BEGIN DOPOST ---");
response.setContentType("text/html; charset=UTF-8");
response.setHeader("Cache-Control","no-cache");
PrintWriter out = response.getWriter();
String typeStr=request.getParameter("Type");
String currentPath=baseDir+typeStr;
String currentDirPath=getServletContext().getRealPath(currentPath);
currentPath=request.getContextPath()+currentPath;
if (debug) System.out.println(currentDirPath);
String retVal="0";
String newName="";
String fileUrl="";
String errorMessage="";
if(enabled) {
DiskFileUpload upload = new DiskFileUpload();
try {
List items = upload.parseRequest(request);
Map fields=new HashMap();
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField())
fields.put(item.getFieldName(),item.getString());
else
fields.put(item.getFieldName(),item);
}
FileItem uplFile=(FileItem)fields.get("NewFile");
String fileNameLong=uplFile.getName();
fileNameLong=fileNameLong.replace('//','/');
String[] pathParts=fileNameLong.split("/");
String fileName=pathParts[pathParts.length-1];
String nameWithoutExt=getNameWithoutExtension(fileName);
String ext=getExtension(fileName);
File pathToSave=new File(currentDirPath,fileName);
fileUrl=currentPath+"/"+fileName;
if(extIsAllowed(typeStr,ext)) {
int counter=1;
while(pathToSave.exists()){
newName=nameWithoutExt+"("+counter+")"+"."+ext;
fileUrl=currentPath+"/"+newName;
retVal="201";
pathToSave=new File(currentDirPath,newName);
counter++;
}
uplFile.write(pathToSave);
}
else {
retVal="202";
errorMessage="";
if (debug) System.out.println("Invalid file type: " + ext);
}
}catch (Exception ex) {
if (debug) ex.printStackTrace();
retVal="203";
}
}
else {
retVal="1";
errorMessage="This file uploader is disabled. Please check the WEB-INF/web.xml file";
}
out.println("<script type="/"text/javascript/"">");
out.println("window.parent.OnUploadCompleted("+retVal+",'"+fileUrl+"','"+newName+"','"+errorMessage+"');");
out.println("</script>");
out.flush();
out.close();
if (debug) System.out.println("--- END DOPOST ---");
}
我們要做的就是在String currentPath=baseDir+typeStr;這一句之後加入從Session中取出用戶名,並添加到currentPath字符串之後的操作,如代碼中加亮部分所示。
2、打開FCKeditor-2.3/src/com/fredck/FCKeditor/connector目錄中的ConnectorServlet.java文件,進行同法處理。
最後,重新編譯打包即可,記得一定要import com.xkland.domain.User類才能編譯通過哦。
至此,經過簡單的修改即可實現我們想要的功能。
# re: SpringSide開發實戰(七):在項目中整合FCKeditor 回覆 更多評論
2007-07-15 09:32 by MagicBlack請問一下這個代碼 直接複製到文件裏就能用嗎?
失去焦點 事件怎麼添加進去???
失去焦點事件是怎麼加進去的呢?因爲在編輯器加載完成後,它會自動調用FCKeditor_OnComplete事件,而我們就是在這個事件裏面註冊OnBlur事件的,如下代碼:
function FCKeditor_OnComplete( editorInstance )
{
editorInstance.Events.AttachEvent( 'OnBlur', onEditorBlur ) ;
}
而OnBlur事件就會調用下面的函數
function onEditorBlur(){
var oSelect = $("img_select");
for(var i=oSelect.options.length-1; i>0; i--){
oSelect.options[i] = null;
}
oEditor = FCKeditorAPI.GetInstance('EditorDefault');
var imgs = oEditor.EditorDocument.body.all.tags("img");
for(var i=0; i < imgs.length; i++){
var oOption = document.createElement("option");
oOption.appendChild(document.createTextNode(imgs[i].src));
oSelect.appendChild(oOption);
}
}
我的郵箱;[email protected]