CKEditor + CKFinder
最後一個 HTML 編輯器,我要介紹王牌 CKEditor。
可能是因為版權的關係,我不清楚。目前沒有人把 CKEditor 整合到 NuGet 上。我們必須做一些事,才能在 ASP.NET MVC 上整合 CKEditor。
先下載 CKEditor 3.6.2
這裡請注意,請下載第一個 CKEditor Source 版本即可,不要下載 for xxx 版本,例如,CKEditor for ASP.NET 版本。
解壓縮後,把 ckeditor 目錄整個複製到專案的根目錄之下。
![]() |
圖一:CKEditor 目錄位置 |
然後先一樣準備我們的 Model、Controller / Action 、View。
Model:BlogPostCKEditor.vb
''' <summary> ''' Blog 發表文章類別 ''' </summary> Public Class BlogPostCKEditor ' 標題 Public Property Title() As String ' 發佈日期 Public Property PostedOn() As DateTime ' 標籤 Public Property Tags() As String ' 內容Public Property Content() As String End Class
Controller / Action :BlogPostCKEditorController.vb
Namespace Mvc3HTMLEditorAndDatePicker Public Class BlogPostCKEditorController Inherits System.Web.Mvc.Controller ' ' GET: /BlogPostCKEditor/Create Function Create() As ActionResult Return View() End Function <HttpPost(), ActionName("Create")> Function Create_Post(model As BlogPostCKEditor) As ActionResult ' 在充許 HTML 的欄位,切記必須使用 AntiXSS 4.0 過濾 ViewBag.HtmlContent = Microsoft.Security.Application.Sanitizer.GetSafeHtmlFragment(model.Content) Return View(model) End Function End Class End Namespace
先「建置」,用「強型別」產生 View。
<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Mvc3HTMLEditorAndDatePicker.BlogPostCKEditor)" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> KKBruce : CKEditor HTML編輯器測試 </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>CKEditor HTML編輯器測試</h2> <%-- The following line works around an ASP.NET compiler warning --%> <%: "" %> <script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script> <script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script> <% Using Html.BeginForm() %> <%: Html.ValidationSummary(True) %> <fieldset> <legend>CKEditor HTML編輯器測試</legend> <div class="editor-label"> <%: Html.LabelFor(Function(model) model.Title) %> </div> <div class="editor-field"> <%: Html.EditorFor(Function(model) model.Title) %> <%: Html.ValidationMessageFor(Function(model) model.Title) %> </div> <div class="editor-label"> <%: Html.LabelFor(Function(model) model.PostedOn) %> </div> <div class="editor-field"> <%: Html.EditorFor(Function(model) model.PostedOn) %> <%: Html.ValidationMessageFor(Function(model) model.PostedOn) %> </div> <div class="editor-label"> <%: Html.LabelFor(Function(model) model.Tags) %> </div> <div class="editor-field"> <%: Html.EditorFor(Function(model) model.Tags) %> <%: Html.ValidationMessageFor(Function(model) model.Tags) %> </div> <div class="editor-label"> <%: Html.LabelFor(Function(model) model.Content) %> </div> <div class="editor-field"> <%: Html.TextAreaFor(Function(model) model.Content) %> <%: Html.ValidationMessageFor(Function(model) model.Content) %> </div> <p> <input type="submit" value="Create" /> </p> </fieldset> <% End Using %> <p> 發表內容(顯示HTML標籤):<%: ViewBag.HtmlContent %> </p> <p> 發表內容(顯示HTML效果):<%= Html.Raw(ViewBag.HtmlContent) %> </p> <div> <%: Html.ActionLink("回首頁", "Index","Home") %> </div> </asp:Content>
注意,要把 Content 欄位的 EditorFor() 修改為 TextAreaFor(),接下來我們就來 ASP.NET MVC 裡使用 CKEditor 與 CKFinder 吧!
在 ASP.NET MVC 使用 CKEditor
我們先在 Create.aspx 的 View 中,引用 ckeditor.js 這支 Javascript。
<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script> <script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script> <!-- CKEditor --> <script src="<%: Url.Content("~/ckeditor/ckeditor.js") %>" type="text/javascript"></script>
然後在要使用的欄位下方撰寫啟用的 Javascript。以我們的範例,是要寫在 Content 欄位下方。
<div class="editor-label"> <%: Html.LabelFor(Function(model) model.Content) %> </div> <div class="editor-field"> <%: Html.TextAreaFor(Function(model) model.Content) %> <%: Html.ValidationMessageFor(Function(model) model.Content) %> </div> <script type="text/javascript"> CKEDITOR.replace('<%= ckdata.ClientID %>', {skin : 'kama'}); </script>
以上程式碼是抄的,抄我「ASP.NET + CKEditor + CKFinder 整合教學」,結果呢?CKEditor當然不會運作,問題出在,ASP.NET 與 ASP.NET MVC 本身運作不同,ASP.NET MVC 可沒有 <%= ckdata.ClientID %>,ClientID ???我還不知道 ASP.NET MVC 有 ClientID 這種東西,有知道的高手、前輩、長輩,可以指教一下我。
那怎麼辦?
也很簡單,把網頁「Ctrl + F5」Run一下,看一下Source Code。你會發現,ASP.NET MVC 會直接使用欄位名稱當HTML 屬性 id 及 name 名稱的值,而不是使用 ASP.NET 控制項的方式。我們只要直接寫欄位名稱即可。
<script type="text/javascript"> CKEDITOR.replace('Content', {skin : 'kama'}); </script>
建置、Ctrl + F5,把網站Run起來,輸入「http://localhost:6944//BlogPostCKEditor/Create」(記得,Port 要換成你的),
![]() |
圖二:CKEditor 運作圖 |
CKEditor 中加入 CKFinder
Step 1. 要在 CKEditor 中加入 CKFinder 功能,先下載 CKFinder for ASP.NET (2.1),然後解壓縮後,把 ckfinder 目錄整個放到 ckeditor 目錄之下。
![]() |
圖三:CKFinder 目錄位置 |
Step 2. CKFinder預設使用"/ckfinder/userfiles/"目錄,來讓使用者當成上傳與下載的目錄,我們另外在根目錄新增一個 Files 目錄,當來上傳與下載的目錄。新增之後,我們必須檢查上傳目錄權限(Files),必須有寫入的權限。(測試環境,最簡單是給此目錄 Everyone 寫入權限,不過切記,上線後的 Server 就不能這樣給,不然…應該會死的很慘)。一般是給 IUSR_<computername> 這個帳號權限(請參考:IIS Authentication Methods)。
Step 3. 將「~\ckeditor\ckfinder\bin\Release\CKFinder.dll」加入參考。
![]() |
圖四:加入 CKFinder.dll 參考 |
<!-- CKEditor 與 CKFinder --> <script src="<%: Url.Content("~/ckeditor/ckeditor.js") %>" type="text/javascript"></script> <script src="<%: Url.Content("~/ckeditor/ckfinder/ckfinder.js") %>" type="text/javascript"></script>
Step 5. 修改 ckfinder\config.ascx 設定檔
需要修改的有三個地方:
- 將 CheckAuthentication() 方法內的 return false; 改為 return true;
作者苦口婆心請大家不要設為「true;」,請大家注意,測試時還沒關係,正式上線一定一定要設為false。 - BaseUrl 修改為上傳路徑(URL)
- BaseDir 修改為上傳路徑(實體路徑)
- BaseUrl = "http://localhost:6944/Files/";
- BaseDir = "D:/StudyTest/Mvc3HTMLEditorAndDatePicker/Mvc3HTMLEditorAndDatePicker/Files/";
注意,第二個路徑,必須使用「/」而不是「\」。另,最後會有一個專案檔讓大家下載,記得要修改以上兩項資訊,範例才能正常運作。
Step 6. 改寫 CKEditor 的 Script
$(function () { CKEDITOR.replace('Content', { skin: 'kama', filebrowserBrowseUrl: '<%: Url.Content("~/ckeditor/ckfinder/ckfinder.html") %>', filebrowserImageBrowseUrl: '<%: Url.Content("~/ckeditor/ckfinder/ckfinder.html?type=Images") %>', filebrowserFlashBrowseUrl: '<%: Url.Content("~/ckeditor/ckfinder/ckfinder.html?type=Flash") %>', filebrowserUploadUrl: '<%: Url.Content("~/ckeditor/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Files") %>', filebrowserImageUploadUrl: '<%: Url.Content("~/ckeditor/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Images") %>', filebrowserFlashUploadUrl: '<%: Url.Content("~/ckeditor/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Flash") %>' }); });
注意,路徑是否正確。
Step 7. 建置,reload 網頁。
![]() |
圖五:Link, Image, Flash 會與 CKFinder 整合 |
![]() |
圖六:Image 影像管理 |
![]() |
圖七:Image 伺服器端管理(點擊看大圖) |
另注意,版權說明。
瀏覽伺服器中,Upadload 是使用一支 Uploader.swf 來上傳,所以沒有任何問題。但在"上傳"之中,是透過 connector.aspx 方式來上傳,我所抓到的網址為「POST http://localhost:6944/ckeditor/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Images&CKEditor=Content&CKEditorFuncNum=2&langCode=zh」得到 500 Internal Server Error。
訊息為:
<html> <head> <title>已發生類型 'CKFinder.Connector.ConnectorException' 的例外狀況。</title> <style> body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;} p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px} b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px} H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red } H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon } pre {font-family:"Lucida Console";font-size: .9em} .marker {font-weight: bold; color: black;text-decoration: none;} .version {color: gray;} .error {margin-bottom: 10px;} .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; } </style> </head> <body bgcolor="white"> <span><H1>'/' 應用程式中發生伺服器錯誤。<hr width=100% size=1 color=silver></H1> <h2> <i>已發生類型 'CKFinder.Connector.ConnectorException' 的例外狀況。</i> </h2></span> <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif "> <b> 描述: </b>在執行目前 Web 要求的過程中發生未處理的例外情形。請檢閱堆疊追蹤以取得錯誤的詳細資訊,以及在程式碼中產生的位置。 <br><br> <b> 例外詳細資訊: </b>CKFinder.Connector.ConnectorException: 已發生類型 'CKFinder.Connector.ConnectorException' 的例外狀況。<br><br> <b>原始程式錯誤:</b> <br><br> <table width=100% bgcolor="#ffffcc"> <tr> <td> <code> 在執行目前 Web 要求期間,產生未處理的例外狀況。如需有關例外狀況來源與位置的資訊,可以使用下列的例外狀況堆疊追蹤取得。</code> </td> </tr> </table> <br> <b>堆疊追蹤:</b> <br><br> <table width=100% bgcolor="#ffffcc"> <tr> <td> <code><pre> [ConnectorException: 已發生類型 'CKFinder.Connector.ConnectorException' 的例外狀況。] CKFinder.Connector.Connector.OnLoad(EventArgs e) in D:\CKFinder.Net\Connector\Connector.cs:222 System.Web.UI.Control.LoadRecursive() +54 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3488 </pre></code> </td> </tr> </table> <br> <hr width=100% size=1 color=silver> <b>版本資訊:</b> Microsoft .NET Framework 版本:4.0.30319; ASP.NET 版本:4.0.30319.17020 </font> </body> </html> <!-- [ConnectorException]: 已發生類型 'CKFinder.Connector.ConnectorException' 的例外狀況。 於 CKFinder.Connector.Connector.OnLoad(EventArgs e) 於 D:\CKFinder.Net\Connector\Connector.cs: 行 222 於 System.Web.UI.Control.LoadRecursive() 於 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) [HttpUnhandledException]: 已發生類型 'System.Web.HttpUnhandledException' 的例外狀況。 於 System.Web.UI.Page.HandleError(Exception e) 於 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) 於 System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) 於 System.Web.UI.Page.ProcessRequest() 於 System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) 於 System.Web.UI.Page.ProcessRequest(HttpContext context) 於 ASP.ckeditor_ckfinder_core_connector_aspx_connector_aspx.ProcessRequest(HttpContext context) 於 g:\Temp\VS\root\946b3876\15d73a2a\App_Web_uyf1d151.0.cs: 行 0 於 System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 於 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) -->
看起來是因為我們是 ASP.NET MVC 的關係,那支上傳程式是為 ASP.NET 所寫,所以無法直接在 ASP.NET MVC 裡運作,CKFinder 細部問題,我就不往下追了。希望未來能有 CKEditor + CKFinder for ASP.NET MVC 的版本出現。
或是有人知道原因和解決辦法,請通知我一下。
CKEditor 中簡易設定 CKFinder
上面的 CKFinder 的 Script 很長,也很容易出錯,我們可以有更簡易的方式來設定 CKFinder。
<script type="text/javascript"> //CKEDITOR.replace('Content', { skin: 'kama' }); /* $(function () { CKEDITOR.replace('Content', { skin: 'kama', filebrowserBrowseUrl: '<%: Url.Content("~/ckeditor/ckfinder/ckfinder.html") %>', filebrowserImageBrowseUrl: '<%: Url.Content("~/ckeditor/ckfinder/ckfinder.html?type=Images") %>', filebrowserFlashBrowseUrl: '<%: Url.Content("~/ckeditor/ckfinder/ckfinder.html?type=Flash") %>', filebrowserUploadUrl: '<%: Url.Content("~/ckeditor/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Files") %>', filebrowserImageUploadUrl: '<%: Url.Content("~/ckeditor/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Images") %>', filebrowserFlashUploadUrl: '<%: Url.Content("~/ckeditor/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Flash") %>' }); }); */ var editor = CKEDITOR.replace('Content'); CKFinder.SetupCKEditor(editor, '<%: Url.Content("~/ckeditor/ckfinder/") %>');最後面簡單兩行,就可以把 CKFinder 設定進去 CKEditor。
CKEditor + CKFinder 使用心得
CKEditor + CKFinder 是一個很棒的組合,而且使用的流暢度也非常不錯,只不過未能像前面的 TinyMCE 或 CLEditor 一樣整合進 NuGet 裡面,所以設定上複雜很多,這是優點也是缺點,優點在於不管是 ASP.NET MVC 1/2/3/4 ... ,也不管是 Visual Studio 2008/2010/11,你都能把 CKEditor + CKFinder 整合進去。使用 NuGet 有一定的版本限制在,這是缺點。
參考資料
- 官網:CKEditor
- 下載:CKEditor 3.6.2
- 下載:CKFinder for ASP.NET (2.1)
- 參考:ASP.NET + CKEditor + CKFinder 整合教學
- 參考:IIS Authentication Methods
http://nuget.org/packages/CKEditor, 已經有人整合進NuGet,但CKFinder還沒有。
回覆刪除你好,想請教用Microsoft.Security.Application.Sanitizer.GetSafeHtmlFragment 之後,用戶輸入內容的格式可能會變,但如要保持用戶的格式,應如何做呢?
回覆刪除Url.Content("~/ckfinder/core/connector/aspx/connector.aspx")?command=QuickUpload&type=Files
回覆刪除後面的參數不要放在Url.Content裡面,不然&會變成 & 會造成上傳錯誤,我也因為這個錯誤而研究好久,終於找到原因
事實證明,MVC是可以用ASPX上傳的