استفاده از Froala WYSIWYG Editor در ASP.NET
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: هشت دقیقه

چندی قبل، معرفی ادیتور سبک وزن و مناسبی را تحت عنوان RedActor، در این سایت ملاحظه کردید. زمانیکه این‌کار انجام شد، این ادیتور هم رایگان بود و هم سورس آخرین نگارش آن به سادگی در دسترس. بعد از مدتی، هر دو ویژگی یاد شده‌ی RedActor حذف شدند. پس از آن ادیتور مدرن و بسیار مناسب دیگری به نام Froala منتشر شد که هرچند نگارش‌های تجاری هم دارد، اما سورس آخرین نگارش آن برای عموم قابل دریافت است. در ادامه مروری خواهیم داشت بر نحوه‌ی یکپارچه سازی آن با ASP.NET MVC و همچنین ASP.NET Web forms.


دریافت آخرین نگارش Froala WYSIWYG Editor
برای دریافت فایل‌های آخرین نگارش این ادیتور وب می‌توانید به سایت آن، قسمت دریافت فایل‌ها مراجعه نمائید.
http://editor.froala.com/download
و یا به این آدرس مراجعه کنید:
https://github.com/froala/wysiwyg-editor/releases 


ساختار پروژه و نحوه‌ی کپی فایل‌های آن
در هر دو مثالی که فایل‌های آن‌را از انتهای بحث می‌توانید دریافت کنید، این ساختار رعایت شده است:


فایل‌های CSS و فونت‌های آن، در پوشه‌ی Content قرار گرفته‌اند.
فایل‌های اسکریپت و زبان آن (که دارای زبان فارسی هم هست) در پوشه‌ی Scripts کپی شده‌اند.

یک نکته
فایل font-awesome.css را نیاز است کمی اصلاح کنید. مسیر پوشه‌ی فونت‌های آن اکنون با fonts شروع می‌شود.


تنظیمات اولیه

تفاوتی نمی‌کند که از وب فرم‌ها استفاده می‌کنید یا MVC، نحوه‌ی تعریف و افزودن پیش نیازهای این ادیتور به نحو ذیل است:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>

    <link href="Content/font-awesome.css" rel="stylesheet" />
    <link href="Content/froala_editor.css" rel="stylesheet" />

    <script src="Scripts/jquery-1.10.2.min.js"></script>
    <script src="Scripts/froala_editor.min.js"></script>
    <script src="Scripts/langs/fa.js"></script>
</head>
<body>
    <form id="form1" runat="server">
    </form>
</body>
</html>
دو فایل CSS دارد (آیکن‌های آن و همچنین شیوه نامه‌ی اصلی ادیتور) به همراه سه فایل JS (جی‌کوئری، ادیتور و فایل زبان فارسی آن) که باید در فایل master یا layout سایت اضافه شوند.


استفاده از Froala WYSIWYG Editor در ASP.NET MVC

در ادامه نحوه‌ی فعال سازی ادیتور وب Froala را در یک View برنامه‌های ASP.NET MVC ملاحظه می‌کنید:
@{
    ViewBag.Title = "Index";
}

<style type="text/css">
    /*تنظیم فونت پیش فرض ادیتور*/
    .froala-element {
    }
</style>

@using (Html.BeginForm(actionName: "Index", controllerName: "Home"))
{
    @Html.TextArea(name: "Editor1")
    <input type="submit" value="ارسال" />
}

@section Scripts
{
    <script type="text/javascript">
        $(function () {
            $('#Editor1').editable({
                buttons: ["bold", "italic", "underline", "strikeThrough", "fontFamily",
                    "fontSize", "color", "formatBlock", "align", "insertOrderedList",
                    "insertUnorderedList", "outdent", "indent", "selectAll", "createLink",
                    "insertImage", "insertVideo", "undo", "redo", "html", "save", "inserthorizontalrule"],
                inlineMode: false,
                inverseSkin: true,
                preloaderSrc: '@Url.Content("~/Content/img/preloader.gif")',
                allowedImageTypes: ["jpeg", "jpg", "png"],
                height: 300,
                language: "fa",
                direction: "rtl",
                fontList: ["Tahoma, Geneva", "Arial, Helvetica", "Impact, Charcoal"],
                autosave: true,
                autosaveInterval: 2500,
                saveURL: '@Url.Action("FroalaAutoSave", "Home")',
                saveParams: { postId: "123" },
                spellcheck: true,
                plainPaste: true,
                imageButtons: ["removeImage", "replaceImage", "linkImage"],
                borderColor: '#00008b',
                imageUploadURL: '@Url.Action("FroalaUploadImage", "Home")',
                imageParams: { postId: "123" },
                enableScript: false
            });
        });
    </script>
}
اگر می‌خواهید فونت پیش فرض آن را تنظیم کنید، باید مطابق کدهای ابتدای فایل، ویژگی‌های froala-element را تغییر دهید.
سپس این ادیتور را بر روی المان TextArea قرار گرفته در صفحه، فعال می‌کنیم.
در قسمت مقادیر buttons، تمام حالات ممکن پیش بینی شده‌اند. هر کدام را که نیاز ندارید، حذف کنید.
نحوه‌ی تعریف زبان و راست به چپ بودن این ادیتور را با مقدار دهی پارامترهای language و direction ملاحظه می‌کنید.

پارامترهای autosave، saveURL و saveParams کار تنظیم ارسال خودکار محتوای ادیتور را جهت ذخیره‌ی آن در سرور به عهده دارند. بر اساس مقدار autosaveInterval می‌توان مشخص کرد که هر چند میلی ثانیه یکبار این‌کار باید انجام شود.
        /// <summary>
        /// ذخیره سازی خودکار
        /// </summary>
        [HttpPost]
        [ValidateInput(false)]
        public ActionResult FroalaAutoSave(string body, int? postId) // نام پارامتر بادی را تغییر ندهید
        {
            //todo: save body ...
            return new EmptyResult();
        }
در قسمت سمت سرور هم می‌توان این مقادیر ارسالی را در اکشن متدی که ملاحظه می‌کنید، دریافت کرد.
چون قرار است تگ‌های HTML به سرور ارسال شوند، ویژگی ValidateInput به false تنظیم شده‌است.
saveParams آن، برای مقدار دهی پارامترهای اضافی است که نیاز می‌باشند تا به سرور ارسال شوند. مثلا شماره مطلب جاری نیز به سرور ارسال گردد.
در اینجا نام پارامتری که ارسال می‌گردد، دقیقا مساوی body است. بنابراین آن‌را تغییر ندهید.

پارامترهای imageUploadURL و imageParams برای فعال سازی ذخیره تصاویر آن در سرور کاربرد دارند.
اکشن متد مدیریت کننده‌ی آن به نحو ذیل می‌تواند تعریف شود:
        // todo: مسایل امنیتی آپلود را فراموش نکنید
        /// <summary>
        /// ذخیره سازی تصاویر ارسالی
        /// </summary>
        [HttpPost]
        public ActionResult FroalaUploadImage(HttpPostedFileBase file, int? postId) // نام پارامتر فایل را تغییر ندهید
        {
            var fileName = Path.GetFileName(file.FileName);
            var rootPath = Server.MapPath("~/images/");
            file.SaveAs(Path.Combine(rootPath, fileName));
            return Json(new { link = "images/" + fileName }, JsonRequestBehavior.AllowGet);
        }
در اینجا نام پارامتری که به سرور ارسال می‌گردد، دقیقا معادل file است. بنابراین آن‌را تغییر ندهید.
خروجی آن برای مشخص سازی محل ذخیره سازی تصویر در سرور باید یک خروجی JSON دارای خاصیت و پارامتر link به نحو فوق باشد (این مسیر، یک مسیر نسبی است؛ نسبت به ریشه سایت).
imageParams آن برای مقدار دهی پارامترهای اضافی است که نیاز می‌باشند تا به سرور ارسال شوند. مثلا شماره مطلب جاری نیز به سرور ارسال گردد.


استفاده از Froala WYSIWYG Editor در ASP.NET Web forms
تمام نکاتی که در قسمت تنظیمات ASP.NET MVC در مورد ویژگی‌های سمت کلاینت این ادیتور ذکر شد، در مورد وب فرم‌ها نیز صادق است. فقط قسمت مدیریت سمت سرور آن اندکی تفاوت دارد.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master"
    ValidateRequest="false"
    EnableEventValidation="false"
    AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FroalaWebFormsTest.Default" %>

<%--اعتبارسنجی ورودی غیرفعال شده چون باید تگ ارسال شود--%>
<%--همچنین در وب کانفیگ هم تنظیم دیگری نیاز دارد--%>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <%--حالت کلاینت آی دی بهتر است تنظیم شود در اینجا--%>
    <asp:TextBox ID="txtEditor" ClientIDMode="Static"
        runat="server" Height="199px" TextMode="MultiLine" Width="447px"></asp:TextBox>
    <br />
    <asp:Button ID="btnSave" runat="server" OnClick="btnSave_Click" Text="ارسال" />

    <style type="text/css">
        /*تنظیم فونت پیش فرض ادیتور*/
        .froala-element {
        }
    </style>

    <script type="text/javascript">
        $(function () {
            $('#txtEditor').editable({
                buttons: ["bold", "italic", "underline", "strikeThrough", "fontFamily",
                    "fontSize", "color", "formatBlock", "align", "insertOrderedList",
                    "insertUnorderedList", "outdent", "indent", "selectAll", "createLink",
                    "insertImage", "insertVideo", "undo", "redo", "html", "save", "inserthorizontalrule"],
                inlineMode: false,
                inverseSkin: true,
                preloaderSrc: 'Content/img/preloader.gif',
                allowedImageTypes: ["jpeg", "jpg", "png"],
                height: 300,
                language: "fa",
                direction: "rtl",
                fontList: ["Tahoma, Geneva", "Arial, Helvetica", "Impact, Charcoal"],
                autosave: true,
                autosaveInterval: 2500,
                saveURL: 'FroalaHandler.ashx',
                saveParams: { postId: "123" },
                spellcheck: true,
                plainPaste: true,
                imageButtons: ["removeImage", "replaceImage", "linkImage"],
                borderColor: '#00008b',
                imageUploadURL: 'FroalaHandler.ashx',
                imageParams: { postId: "123" },
                enableScript: false
            });
        });
    </script>
</asp:Content>
همانطور که ملاحظه می‌کنید،  ValidateRequest صفحه به false تنظیم شده و همچنین در وب کانفیگ httpRuntime requestValidationMode به نگارش 2 تنظیم گردیده‌است تا بتوان توسط این ادیتور تگ‌های ارسالی را به سرور ارسال کرد.
به علاوه ClientIDMode=Static نیز تنظیم شده‌است، تا بتوان از ID تکست باکس قرار گرفته در صفحه، به سادگی در کدهای سمت کاربر جی‌کوئری استفاده کرد.
اگر دقت کرده باشید، save urlها اینبار به فایل FroalaHandler.ashx اشاره می‌کنند. محتوای این Genric handler را ذیل مشاهده می‌کنید:
using System.IO;
using System.Web;
using System.Web.Script.Serialization;

namespace FroalaWebFormsTest
{
    public class FroalaHandler : IHttpHandler
    {
        //todo: برای اینکارها بهتر است از وب ای پی آی استفاده شود
        //todo: یا دو هندلر مجزا یکی برای تصاویر و دیگری برای ذخیره سازی متن

        public void ProcessRequest(HttpContext context)
        {
            var body = context.Request.Form["body"];
            var postId = context.Request.Form["postId"];
            if (!string.IsNullOrWhiteSpace(body) && !string.IsNullOrWhiteSpace(postId))
            {
                //todo: save changes

                context.Response.ContentType = "text/plain";
                context.Response.Write("");
                context.Response.End();
            }

            var files = context.Request.Files;
            if (files.Keys.Count > 0)
            {
                foreach (string fileKey in files)
                {
                    var file = context.Request.Files[fileKey];
                    if (file == null || file.ContentLength == 0)
                        continue;

                    //todo: در اینجا مسایل امنیتی آپلود فراموش نشود
                    var fileName = Path.GetFileName(file.FileName);
                    var rootPath = context.Server.MapPath("~/images/");
                    file.SaveAs(Path.Combine(rootPath, fileName));


                    var json = new JavaScriptSerializer().Serialize(new { link = "images/" + fileName });
                    // البته اینجا یک فایل بیشتر ارسال نمی‌شود
                    context.Response.ContentType = "text/plain";
                    context.Response.Write(json);
                    context.Response.End();
                }
            }

            context.Response.ContentType = "text/plain";
            context.Response.Write("");
            context.Response.End();
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}
در اینجا نحوه‌ی مدیریت سمت سرور auto save و همچنین ارسال تصاویر ادیتور Froala ، ذکر شده‌اند. با استفاده از context.Request.Form می‌توان به عناصر ارسالی به سرور دسترسی پیدا کرد. همچنین توسط context.Request.Files، اگر فایلی ارسال شده بود، ذخیره شده و نهایتا خروجی JSON مدنظر بازگشت داده می‌شود.


یک نکته‌ی امنیتی مهم
<location path="upload">
  <system.webServer>
    <handlers accessPolicy="Read" />
  </system.webServer>
</location>
تنظیم فوق را در web.config سایت، جهت Read only کردن پوشه‌ی ارسال تصاویر، حتما مدنظر داشته باشید. در اینجا فرض شده‌است که پوشه‌ی uploads قرار است قابلیت اجرای فایل‌های پویا را نداشته باشد.


کدهای کامل این مطلب را در ادامه می‌توانید دریافت کنید
Froala-Sample
 
  • #
    ‫۱۰ سال و ۵ ماه قبل، پنجشنبه ۱۸ اردیبهشت ۱۳۹۳، ساعت ۲۲:۴۱
    سلام
    ممنون برای این مطلب
    فکر کنم این ادیتور با کروم یک مشکل داره:
    برای مثال کلمه «تست» رو در اون تایپ کنید، سپس بعد از «س» یک حرف جدید مثل «ن» وارد کنید: نتیجه باید بشود «تسنت» اما میشود «تسن‌ت».
    البته من در کروم 34 تست کردم. با IE 10 و Firefox 28 مشکلی نداشت. 
  • #
    ‫۱۰ سال و ۵ ماه قبل، شنبه ۲۰ اردیبهشت ۱۳۹۳، ساعت ۱۳:۰۰
    عالیه؛ فقط قسمت آپلود فایل را کم داره.
  • #
    ‫۱۰ سال و ۵ ماه قبل، یکشنبه ۲۱ اردیبهشت ۱۳۹۳، ساعت ۱۳:۲۴
    برای Upload تصویر به صورتی که در Sample خود ادیتور با Progress انجام می‌شود چه باید کرد؟
    • #
      ‫۱۰ سال و ۵ ماه قبل، یکشنبه ۲۱ اردیبهشت ۱۳۹۳، ساعت ۱۳:۳۹
      سمت کلاینت آن به صورت پیش فرض فعال است و نیاز به تنظیم اضافه‌تری ندارد. فقط باید مدیریت سمت سرور آن‌را مطابق کدهایی که در مطلب جاری توضیح داده شدند، انجام دهید.
  • #
    ‫۱۰ سال و ۴ ماه قبل، جمعه ۹ خرداد ۱۳۹۳، ساعت ۱۵:۴۵
    با سلام . برای ایجاد بلاک کد که در redactor موجود است، در این ادیتور چه باید کرد؟

    • #
      ‫۱۰ سال و ۴ ماه قبل، جمعه ۹ خرداد ۱۳۹۳، ساعت ۱۸:۳۳
      امکان افزونه نویسی هم دارد. اگر همان کدهای افزونه‌ای را که برای redactor نوشتیم، با این ادیتور تطابق دهیم، به دکمه‌ی سفارشی زیر خواهیم رسید:
      buttons: [
                       // .... ,
                       "insertHTML" //custom button
               ],
      
      
           customButtons: {
                          insertHTML: {
                              title: 'Insert Code',
                              icon: {
                                  type: 'font',
                                  value: 'fa fa-dollar' // Font Awesome icon class fa fa-*
                              },
                              callback: function (editor) {
                                  editor.saveSelection();
      
                                  var codeModal = $("<div>").addClass("froala-modal").appendTo("body");
                                  var wrapper = $("<div>").addClass("f-modal-wrapper").appendTo(codeModal);
                                  $("<h4>").append('<span data-text="true">Insert Code</span>')
                                      .append($('<i class="fa fa-times" title="Cancel">')
                                      .click(function () {
                                          codeModal.remove();
                                      }))
                                      .appendTo(wrapper);
      
                                  var dialog = "<textarea id='code_area' style='height: 211px; width: 538px;' /><br/><label>Language:</label><select id='code_lang'><option>CSharp</option><option>VB</option><option>JScript</option><option>Sql</option><option>XML</option><option>CSS</option><option>Java</option><option>Delphi</option></select> <input type='button' name='insert' id='insert_btn' value='Insert' /><br/>";
                                  $(dialog).appendTo(wrapper);
      
                                  $("#code_area").text(editor.text());
      
                                  if (!editor.selectionInEditor()) {
                                      editor.$element.focus();
                                  }
      
                                  $('#insert_btn').click(function () {
                                      var lang = $("#code_lang").val();
                                      var code = $("#code_area").val();
                                      code = code.replace(/\s+$/, ""); // rtrim
                                      code = $('<span/>').text(code).html(); // encode    
      
                                      var htmlCode = "<pre language='" + lang + "' name='code'>" + code + "</pre></div>";
                                      var codeBlock = "<div align='left' dir='ltr'>" + htmlCode + "</div><br/>";
      
                                      editor.restoreSelection();
                                      editor.insertHTML(codeBlock);
                                      editor.saveUndoStep();
      
                                      codeModal.remove();
                                  });
                              }
                          }
                      }
      • #
        ‫۱۰ سال و ۴ ماه قبل، یکشنبه ۱۱ خرداد ۱۳۹۳، ساعت ۰۵:۴۳
        آقای نصیری با تشکر از معرفی این ویرایش گر و ارائه این تکه کد. من این کد رو در گیت هاب دیدم که به اشتراک گذاشتید. به نظرم مشکلی با SyntaxHighliter داشت که در اینجا مطرح کردم. 
        با تغییری که اعمال کردم در وبلاگ من کار کرد و فوق العاده بود.
        با تشکر از به اشتراگ گزاری دانش تون
      • #
        ‫۹ سال و ۱۱ ماه قبل، جمعه ۲۵ مهر ۱۳۹۳، ساعت ۱۴:۴۹
        سلام. من از این قطعه کدی که شما گذاشتید برای ایجاد بلاک کد استفاده میکنم و درست هم کار میکنه منتها یک مشکلی که دارم این هستش که وقتی فرم رو پست میکنم تمام کد رو فقط در یک خط نشون میده. درحالی که اگر برم توی دیتابیسم و داخل تگ pre کدم رو پیست کنم درست نشون میده. اگر نکته ای داره ممنون میشم بهم بگید. تو اینترنت هم خیلی گشتم ولی چیز خاصی پیدا نکردم.
        • #
          ‫۹ سال و ۱۱ ماه قبل، جمعه ۲۵ مهر ۱۳۹۳، ساعت ۱۵:۱۳
          مشکل CSS دارید. سطر:
          var htmlCode = "<pre language='" + lang + "' name='code'>" + code + "</pre></div>";
          مطابق تنظیمات شخصی است. اینجا class لازم را بر اساس تنظیمات CSS نمایشگر کدهای خود اضافه کنید (مثلا 'class='brush: csharp).
          var htmlCode = "<pre class='brush: " + lang + "' language='" + lang + "' name='code'>" + code + "</pre></div>";
          • #
            ‫۹ سال و ۱۱ ماه قبل، جمعه ۲۵ مهر ۱۳۹۳، ساعت ۱۵:۴۱
            بله من قبلاً این کار رو کردم تا استایل نمایش کد درست شد ولی الان مشکلی که هست فقط اینه که چندین خط کدی که پیست میکنم همش توی یک خط نمایش داده میشه و اسکرول افقی میخوره. مثل شکل زیر:

            • #
              ‫۹ سال و ۱۱ ماه قبل، جمعه ۲۵ مهر ۱۳۹۳، ساعت ۱۵:۴۸
              مشکل این ادیتور یا افزونه‌ی نوشته شده نیست. اگر از یک فشرده کننده HTML استفاده می‌کنید، ممکن است خطوط جدید را حذف کرده باشد. برای مثال این مورد در «بلاگر» گوگل مرسوم است. راه حل آن:
              SyntaxHighlighter.config.bloggerMode = true;
              SyntaxHighlighter.all();
              برای مطالعه بیشتر
      • #
        ‫۹ سال و ۷ ماه قبل، پنجشنبه ۲۳ بهمن ۱۳۹۳، ساعت ۱۹:۳۰
        بر اساس این باید از this به جای editor استفاده کنیم.
        من به این صورت استفاده کردم 
         insertHTML: {
                                        title: 'Insert Code',
                                        icon: {
                                            type: 'font',
                                            value: 'fa fa-dollar' // Font Awesome icon class fa fa-*
                                        },
                                        callback: function (editor) {
                                            this.saveSelection();
                                            var thisEditor = this;
                                            var codeModal = $("<div>").addClass("froala-modal").appendTo("body");
                                            var wrapper = $("<div>").addClass("f-modal-wrapper").appendTo(codeModal);
                                            $("<h4>").append('<span data-text="true">Insert Code</span>')
                                                .append($('<i class="fa fa-times" title="Cancel">')
                                                    .click(function () {
                                                        codeModal.remove();
                                                    }))
                                                .appendTo(wrapper);
         
                                            var dialog = "<textarea id='code_area' style='height: 211px; width: 538px;' /><label>Language:</label><select id='code_lang'><option>CSharp</option><option>VB</option><option>JScript</option><option>Sql</option><option>XML</option><option>CSS</option><option>Java</option><option>Delphi</option></select> <input type='button' name='insert' id='insert_btn' value='Insert' />";
                                            $(dialog).appendTo(wrapper);
         
                                            $("#code_area").text(this.text());
         
                                            if (!this.selectionInEditor()) {
                                                this.$element.focus();
                                            }
         
                                            $('#insert_btn').click(function () {
                                                var lang = $("#code_lang").val();
                                                var code = $("#code_area").val();
                                                code = code.replace(/\s+$/, ""); // rtrim
                                                code = $('<span/>').text(code).html(); // encode   
                                                var htmlCode = "<pre class='brush: " + lang.toLowerCase() + "' language='" + lang + "' name='code'>" + code + "</pre></div>"; // syntaxhighlighter با این کد هماهنگ است
                                                //var htmlCode = "<pre language='" + lang + "' name='code'>" + code + "</pre></div>";
                                                var codeBlock = "<div align='left' dir='ltr'>" + htmlCode + "</div>";
         
                                                thisEditor.restoreSelection();
                                                thisEditor.insertHTML(codeBlock);
                                                thisEditor.saveUndoStep();
         
                                                codeModal.remove();
                                            });
                                        }
                                    }

      • #
        ‫۹ سال و ۶ ماه قبل، دوشنبه ۲۵ اسفند ۱۳۹۳، ساعت ۲۰:۰۰
        اما این کدی که به عنوان custombutton گذاشتین رو توی کدوم فایل و به چه صورت و به کجای فایلی که معرفی می‌فرمایید باید وارد کرد؟
        ، اجازه بفرمایید تصحیح کنم ، 
        این کد را مطابق دستور شما اضافه کردم اما زمانی کلیک میکنم هیچ اتفاقی نمیافته
        • #
          ‫۹ سال و ۶ ماه قبل، دوشنبه ۲۵ اسفند ۱۳۹۳، ساعت ۲۰:۰۸
          - سورس انتهای بحث را دریافت کنید. به خاصیت buttons آن (که در View برنامه موجود است)، مقدار insertHTM اضافه می‌شود. همچنین یک خاصیت جدید هم به نام customButtons با مقداری که مشخص شده، باید در کنار سایر خواص این افزونه، اضافه شود.
          - سایر نظرات تکمیلی و نگارش‌های بهبود یافته را هم مدنظر داشته باشید.
          • #
            ‫۹ سال و ۶ ماه قبل، دوشنبه ۲۵ اسفند ۱۳۹۳، ساعت ۲۱:۲۳
            از راهنمایی شما بسیار متشکرم. دو مورد برای من پیش اومد؛ یکی اینکه با استفاده از راهنمایی‌های شما دکمه کد اضافه شد، اما اول اینکه زمانی روی آن کلیک میکنم هیچ اتفاقی نمیافته. با دو browser چک کردم و هیچ تغییری نکرد. مطلب دوم: در local  آیکن‌ها نمایش داده می‌شوند اما روی هاست آیکن‌ها نمایش داده نمی‌شوند. در ابتدا از bundle استفاده می‌کردم اما با اینکه لینک مستقیم فایل‌ها را هم گذاشتم باز همان مشکل را داشتم. البته فایل اصلی jquery را هم به ابتدای صفحه انتقال دادم، اما باز هم تغییری نکرد.
            • #
              ‫۹ سال و ۶ ماه قبل، سه‌شنبه ۲۶ اسفند ۱۳۹۳، ساعت ۰۲:۴۴
              کدهای این مطلب به مخزن کد Froala-Sample منتقل شدند.
              - نحوه‌ی تعریف دکمه‌ی کد در اینجا
              - آیکن‌ها از پوشه‌ی fonts قرار گرفته در ریشه‌ی سایت خوانده می‌شوند. اگر سرور قادر به نمایش آن‌ها نیست به احتمال زیاد mime type آن‌ها را تعریف نکرده‌است (^ و ^ و ^).
              <system.webServer>
                ...
                <staticContent>
                  <remove fileExtension=".woff" />
                  <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
                  <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
                  <mimeMap fileExtension=".otf" mimeType="font/otf" />
                  <mimeMap fileExtension=".svg" mimeType="images/svg+xml" />
                  <mimeMap fileExtension=".svgz" mimeType="images/svg+xml" />
                </staticContent>
              </system.webServer>
  • #
    ‫۱۰ سال و ۳ ماه قبل، یکشنبه ۱ تیر ۱۳۹۳، ساعت ۱۵:۵۷
    قطعه کد  برای fullscreen این ادیتور:
    fullscreen: {
                            title: 'FullScreen',
                            icon: {
                                type: 'font',
                                value: 'fa fa-external-link'
                            },
                            callback: function (editor) {  
                                if (editor.$box.hasClass("froala-editor-full-screen")) {
                                    editor.$box.removeClass("froala-editor-full-screen");
                                    editor.$box.appendTo(editor.editorParent);
                                }
                                else {
                                    if (editor.editorParent === undefined)
                                        editor.editorParent = editor.$box.parent();
                                    editor.$box.addClass("froala-editor-full-screen");
                                    editor.$box.appendTo("body");
                                }
                                editor.focus();
                            }
                        },
    و کلاس css مربوطه :
    .froala-editor-full-screen {
        position: fixed !important;
        z-index: 10000;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        background-color: #fff;
    }
    
        .froala-editor-full-screen div.f-placeholder {
            height: 100% !important;
        }

  • #
    ‫۱۰ سال و ۲ ماه قبل، سه‌شنبه ۲۴ تیر ۱۳۹۳، ساعت ۱۸:۱۳
    سلام و ممنون از انتشار مطالب مفیدتون
    یک سوال : بنده (بنا به سفارش مشتری) احتیاج به ادیتوری دارم که در ثبت لینک امکان flow و noflow رو داشته باشه
    تا به حال خودم این امکان رو ندیدم. شما موردی رو مشاهده کردید و یا میشه همین ادیتور رو دستکاری کرد ؟
      • #
        ‫۱۰ سال و ۲ ماه قبل، سه‌شنبه ۲۴ تیر ۱۳۹۳، ساعت ۱۸:۴۲
        ممنون از پاسخ تان
        اما این برای کلیه لینک‌ها می‌باشد من می‌خوام موردی به یک لینک این امکان داده شده و به یکی دیگه داده نشده 
        Enables or disables adding the  rel="nofollow"  attribute on all links. 
        • #
          ‫۱۰ سال و ۲ ماه قبل، سه‌شنبه ۲۴ تیر ۱۳۹۳، ساعت ۱۸:۴۷
          در قسمت مدیریت پروژه آن درخواست دهید تا یک checkbox مخصوص اینکار به صفحه دیالوگ استاندارد افزودن لینک آن اضافه کنند.
  • #
    ‫۱۰ سال و ۱ ماه قبل، دوشنبه ۲۷ مرداد ۱۳۹۳، ساعت ۰۷:۰۰
    با سلام؛ توسط خاصیت imageUploadURL  مشخص میکنیم که تصاویر کجا ذخیره شوند. یک خاصیت دیگه هم imagesLoadURL در خصوص این خاصیت میشه توضیح بدین.
    • #
      ‫۱۰ سال و ۱ ماه قبل، دوشنبه ۲۷ مرداد ۱۳۹۳، ساعت ۱۳:۳۲
      برای فعال سازی media manager آن کاربرد دارد. یک افزونه‌ است که باید جداگانه اضافه شود. مقدار imagesLoadUrl به مسیری در سرور اشاره می‌کند که قرار است آرایه‌ای از مسیرهای تصاویر قابل نمایش در آن‌را به فرمت JSON بازگشت دهد.
      • #
        ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۲۸ مرداد ۱۳۹۳، ساعت ۰۳:۲۵
        برای حذف یک تصویر از قسمت منیجر از خاصیت imageDeleteURL  و برای ارسال پارامترها از imageDeleteParams استفاده می‌کنیم. خاصیت imageDeleteParams را باید به چه صورت مقداردهی کرد
        موقعی که از کد زیر استفاده میکنم ادیتور لود نمیشه
        imageDeleteParams: { src: $img.attr('src') }
        • #
          ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۲۸ مرداد ۱۳۹۳، ساعت ۰۴:۳۷
          - نیازی به تنظیم imageDeleteParams نیست. از آن برای ارسال یک سری اطلاعات اضافی و دستی مانند شماره id کاربر و امثال آن استفاده می‌شود. src تصویری که قرار است حذف شود، به صورت خودکار به مسیر مشخص شده توسط imageDeleteURL در سمت سرور ارسال می‌شود.
          - برای تشخیص دقیق نام پارامترهای ارسالی به سرور از برگه‌ی network فایرباگ استفاده کنید.
          - روش دوم تشخیص پارامترها و مقادیر ارسالی به سرور استفاده از FormCollection است.
          • #
            ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۲۸ مرداد ۱۳۹۳، ساعت ۱۹:۴۵
            ممنون؛ پارامتر src که آدرس تصویر رو در خودش داره به سمت سرور ارسال میشه.
  • #
    ‫۱۰ سال و ۱ ماه قبل، چهارشنبه ۲۹ مرداد ۱۳۹۳، ساعت ۰۲:۴۲
    با سلام

    موقعی که از ادیتور توی یک modal استفاده میکنم تکست باکس‌های ادیتور غیر قابل فوکوس میشن
    z-index رو هم تغییر دادم اما تاثیری نداشت و موقع کلیک ، فوکوس به تکست باکس‌های دیگه که روی modal هستند منتقل میشه

  • #
    ‫۱۰ سال و ۱ ماه قبل، یکشنبه ۲ شهریور ۱۳۹۳، ساعت ۱۶:۳۵
    سلام.
    من این ادیتورو گذاشتم. فوق العاده راحت و قشنگه. فقط با بخش ارسال عکسش مشکل دارم.
    مسئله اینکه عکس من توی پوشه مورد نظرم داره ذخیره میشه ولی اشکالش اینکه توی متنم موقع نمایش هیچی نمیاید. همه خصوصیات مثلا ایتالیک و بولد به متون اعمال میشن ولی عکس نیست!
  • #
    ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۴ شهریور ۱۳۹۳، ساعت ۱۳:۲۷
    سلام 
    اگه داخل ادیتور متنی نوشته نشه مقدار پیش فرضش 
    "<p><br></p>"
    هست که موقع اعتبار سنجی معتبره.
    تو تنظیماتش هم گزینه ای برای خالی کردن ادیتور وجود نداره. 
  • #
    ‫۱۰ سال و ۱ ماه قبل، دوشنبه ۱۷ شهریور ۱۳۹۳، ساعت ۱۷:۵۴
    امکان آپلود فایل هم به آن اضافه شده: ^ و ^
  • #
    ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۱۸ شهریور ۱۳۹۳، ساعت ۱۴:۱۳
    با سلام
    مثل اینکه این ادیتور دیگه رایگان نیست :(
    می خواستم بدونم اگه از همان سورسی که برای دانلود گذاشتیند استفاده کنم مشکلی بوجود نمیاد؟
    • #
      ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۱۸ شهریور ۱۳۹۳، ساعت ۱۴:۴۶
      این مسایل مشکل ما نیست. چون فلان ابزار رایگان هست یا نیست، دلیلی بر این نیست و نخواهد بود که ما در مورد آن مطلبی تهیه نکنیم یا کاری انجام ندهیم.
  • #
    ‫۹ سال و ۱۲ ماه قبل، سه‌شنبه ۸ مهر ۱۳۹۳، ساعت ۲۰:۴۹
    سلام؛ چطوری می‌توان به این ویرایشگر قابلیت Code اضافه کرد آیا شما قبلا تجربه همچین کاری را دارید و یا حتی جایی دیده‌اید؟
  • #
    ‫۹ سال و ۹ ماه قبل، یکشنبه ۱۴ دی ۱۳۹۳، ساعت ۲۲:۵۳
    سلام؛ در زمان استفاده از Ajax.BeginForm  در صورتی که از این ادیتور هم استفاده کنیم عمل POST انجام نمی‌شود. به چه دلیل؟ در شرایطی که با Html.BeginForm  چنین مشکلی وجود ندارد.
    • #
      ‫۹ سال و ۹ ماه قبل، دوشنبه ۱۵ دی ۱۳۹۳، ساعت ۰۱:۲۱
      در مثالی که پیوست شد، از فایل jquery.unobtrusive-ajax استفاده نشده‌است. ابتدا این فایل را اضافه کنید:
      PM> Install-Package Microsoft.jQuery.Unobtrusive.Ajax
      بعد مدخل تعریف آن هم باید به فایل _Layout.cshtml اضافه شود:
      <script src="~/Scripts/jquery-1.10.2.min.js"></script>
      <script src="~/Scripts/jquery.validate.min.js"></script>
      <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
      <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
      به این ترتیب Ajax.BeginForm کار خواهد کرد (آزمایش شد).
  • #
    ‫۹ سال و ۶ ماه قبل، پنجشنبه ۱۳ فروردین ۱۳۹۴، ساعت ۰۵:۰۷
    سلام. من در حال نوشتن یک سیستم CMS هستم و برای نوشتن مطلب از این ادیتور استفاده میکنم. وقتی مطلب رو ارسال میکنم و میخوام در قسمت مطالب نشون بدم، محتوای نوشته به صورت کد HTML نشون داده میشه، میخواستم بدونم چطور میتونم این مشکل رو برطرف کنم؟
    • #
      ‫۹ سال و ۶ ماه قبل، پنجشنبه ۱۳ فروردین ۱۳۹۴، ساعت ۰۶:۱۱
      باید از متد Html.Raw@ برای جلوگیری از encoding خودکار razor استفاده کنید.
  • #
    ‫۹ سال و ۴ ماه قبل، شنبه ۲ خرداد ۱۳۹۴، ساعت ۲۲:۱۶
    سلام؛ اگه کنترلر توی یک Area باشه چطوری میتونیم مسیر عکس‌های ذخیره شده تو ریشه سایت رو به فرمت Json برگردونیم؟ مثلا ما یک Area داریم به نام Admin و کنترلر Post هم تو این Area هست. من وقتی عکس رو آپلود میکنم چیزی نمایش داده نمیشه رفتم تو حالت Developer مرورگر دیدم همچین خطایی میده :
    http://localhost:1455/Admin/Post/Content/Images/a_sunny_days_end-wallpaper-1440x900.jpg 404 (Not Found) 
    دلیلش هم اینه که تو متد FroalaUploadImage مقدار Json برگشتی به صورت زیر هست :
    return Json(new { link = "Content/Images/" + fileName }, JsonRequestBehavior.AllowGet);
      که اشاره به مسیر بالا داره. من میخوام خروجی Jsonبه صورت زیر باشه
    http://localhost:1455/Content/Images/a_sunny_days_end-wallpaper-1440x900.jpg

    • #
      ‫۹ سال و ۴ ماه قبل، یکشنبه ۳ خرداد ۱۳۹۴، ساعت ۰۰:۴۰
      از این روش برای تولید url استفاده کنید:
      new UrlHelper(this.Request.RequestContext).Content("~/content/images/file.png")
  • #
    ‫۸ سال و ۱۰ ماه قبل، سه‌شنبه ۱۰ آذر ۱۳۹۴، ساعت ۲۰:۵۶
    با سلام.
    من ورژن جدید این ادیتور رو امتحان کردم و وقتی که direction=rtl ست میشه ، ردیف رنگها به هم میریزه و هر بار که color picker رو باز میکنم تعداد رنگ‌ها تو یه ردیف کمتر میشه تا نهایت لیست رنگ‌ها رو عمودی نمایش می‌دهد .می خواستم بدونم کسی با این مشکل رو به رو شده و اگه آره راه حلی داره؟
    با تشکر
  • #
    ‫۷ سال و ۷ ماه قبل، سه‌شنبه ۱۹ بهمن ۱۳۹۵، ساعت ۱۴:۳۱
    برای حذف فایل‌ها به صورت فیزیکی از سرور، زمانی که لینک را از ادیتور حذف می‌کنید و یا حتی با backSpace لینک مربوط به فایلی را حذف می‌کنید می‌توان از اسکریپت زیر بهره جست:
    .on('froalaEditor.file.unlink', function (e, editor, link) {
                    $.ajax({
                        method: "POST",
                        url: "@Url.Action("FroalaDeleteFile", "YeChizis")",
                        data: {
                            src:link.href
                        }
                    })
                    .done(function (data) {
                        console.log('file was deleted');
                    })
                    .fail(function () {
                        console.log('file delete problem');
                    })
                })
    و در سمت سرور داریم:
            /// <summary>
            /// حذف فایل‌های ادیتور
            /// </summary>
            [HttpPost]
            public void FroalaDeleteFile(string src)
            {
                string relativePath = new Uri(src).PathAndQuery;
                string physicalFilePath = Server.MapPath(relativePath);
                if (System.IO.File.Exists(physicalFilePath))
                    System.IO.File.Delete(physicalFilePath);
            }
    همچنین اگر نیاز باشد که در صورت حذف عکس از ادیتور، آن عکس به صورت فیزیکی نیز از سرور پاک شود می‌توان از اسکریپت زیر بهره جست:
    البته کمی بالاتر آقای نصیری اشاره کرده اند اما مشروح آن به شکل زیر است:
    .on('froalaEditor.image.removed', function (e, editor, $img) {
                    $.ajax({
                        // Request method.
                        method: "POST",
    
                        // Request URL.
                        url: "@Url.Action("FroalaDeleteImageAction", "ControllerName")",
    
                        // Request params.
                        data: {
                            src: $img.attr('src')
                        }
                    })
                    .done(function (data) {
                        console.log('image was deleted');
                    })
                    .fail(function () {
                        console.log('image delete problem');
                    })
                })

      و در سمت سرور Action ای مانند زیر:
            /// <summary>
            /// حذف عکس‌های ادیتور
            /// </summary>
            [HttpPost]
            public void FroalaDeleteImageAction(string src)
            {
                string physicalFilePath = Server.MapPath(src);
                if (System.IO.File.Exists(physicalFilePath))
                    System.IO.File.Delete(physicalFilePath);
            }
    • #
      ‫۷ سال و ۷ ماه قبل، سه‌شنبه ۱۹ بهمن ۱۳۹۵، ساعت ۱۴:۵۹
      - با تشکر از نکته‌ی تکمیلی شما.
      - فقط در حین delete و کلا کار با مسیرهای دریافتی از کاربران، نکته‌ی «باگ Directory Traversal در سایت» را حتما مدنظر داشته باشید.
      - به علاوه اینکه ممکن است کاربری شروع کند به درج مسیرهایی که قبلا وجود داشته‌اند و درخواست حذف یکی یکی آ‌ن‌ها را ارسال کند. در کل، وجود حذف فیزیکی را به موارد نادر محدود کنید. فایل‌های ارسالی را در قسمت admin سایت لیست کنید (یک گزارش ساده از پوشه‌ی آپلودها) و فقط با دسترسی ادمین امکان حذف را قرار دهید. 
  • #
    ‫۷ سال و ۶ ماه قبل، دوشنبه ۲۳ اسفند ۱۳۹۵، ساعت ۱۸:۳۱
    در ورژن‌های 2 این ادیتور برای تعریف buttonهای toolbar از چندین دستور واکنشگرا، به صورت زیر استفاده می‌شود:
    $('.selector').froalaEditor({
        toolbarButtons: ['bold', 'italic', 'underline'],
        /*
        (≥ 1200px)
        Default ['fullscreen', 'print', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'fontFamily', 'fontSize', '|', 'specialCharacters', 'color', 'emoticons', 'inlineStyle', 'paragraphStyle', '|', 'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', 'insertHR', '-', 'insertLink', 'insertImage', 'insertVideo', 'insertFile', 'insertTable', 'undo', 'redo', 'clearFormatting', 'selectAll', 'html']
        */
        
        toolbarButtonsMD: ['bold', 'italic', 'underline'],
        /*
        (≥ 992px)
        Default ['fullscreen', 'bold', 'italic', 'underline', 'fontFamily', 'fontSize', 'color', 'paragraphStyle', 'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', 'insertHR', 'insertLink', 'insertImage', 'insertVideo', 'insertFile', 'insertTable', 'undo', 'redo', 'clearFormatting']
        */
    
        toolbarButtonsSM: ['bold', 'italic', 'underline'],
         /*
        (≥ 768px)
        Default ['fullscreen', 'bold', 'italic', 'underline', 'fontFamily', 'fontSize', 'insertLink', 'insertImage', 'insertTable', 'undo', 'redo']
        */
    
        toolbarButtonsXS: ['bold', 'italic', 'underline'],
         /*
        (< 768px)
        Default ['bold', 'italic', 'fontFamily', 'fontSize', 'undo', 'redo'] 
        */
    });
      که در صورت عدم توجه به مقادیر اولیه هر کدام، ممکن است درگیر عدم نمایش صحیح buttonها و سردرگمی برای تشخیص مشکل در نمایشگرهای مختلف شوید، که مشکل را باید در رزولوشن‌های متفاوت جستجو کنید!