Прикручиваем WYSIWYG редактор ckeditor c файловым менеджером simogeo в Asp.Net MVC 3

Прикручиваем WYSIWYG редактор ckeditor c файловым менеджером simogeo в Asp.Net MVC 3

Сегодня, уже никто не удивится, услышав задачу "прикрутить редактор статей". Если когда-то сама возможность редактировать тексты в Web на ровне с оконными приложениями, такими как Microsoft Word, казалась запредельной, то, сейчас, такого рода функция есть практически на любом сайте (обычно, в административной панели). Если ее нет, то достаточно открыть любую поисковую систему и найти готовое решение.

Примечание: Конечно, вы можете задуматься над тем, чтобы с нуля реализовать редактор. Но, прежде, чем это делать, стоит подумать о том, что реализация такого механизма займет уйму времени. "И это на еще один велосипед?"

Как оказалось, найти бесплатный wysiwyg редактор со встроенным и так же бесплатным файловым менеджером под Asp.Net MVC 3 - далеко не простая задача. Готовых красивых html-редакторов много. Однако, в большинстве из них менеджер изображений либо платный, либо разработан под php (иногда другой язык). Поэтому задачу приходится делить на три части: подключение wysiwyv редактора, настройка отдельного менеджера файлов и настройка связки редактора с менеджером.

Если с первой задачей все достаточно просто - можно взять любой понравившийся популярный редактор, который включает возможность подключения стороннего менеджера. Коим и стал, CKEditor (скачать с оф. сайта). То с менеджером изображений все оказалось несколько сложнее. 

Примечание: Многофункциональные WYSIWYG редакторы это достаточно сложные механизмы, которые должны учитывать кучу особенностей. Поэтому их использование должно предполагать саму возможность найти ответ в интернете на возникшую проблему. Малоизвестные редакторы часто забрасывают, поэтому со всеми возможными проблемами вам придется справляться в одиночку. Согласитесь, что разбираться в нагромождениях чужого кода - это не самая интересная и приятная задача. А в случае отсутствия несжатых исходников - еще и невеселая. Конечно, вы можете использовать крошечные и небольшие редакторы, но, по большому счету, они не подойдут для написания статей. Скорее это возможность писать небольшие комментарии.

Примечание: При составлении статьи использовался CKEditor 3, так как он заметно меньше весит, чем CKEditor 4.

В сети попалось три более или менее нетребовательных файловых менеджеров: менеджер от simogeo, небольшой самописный шаблон с обертками (который позволяет только просматривать файлы) и elFinder. Однако, полноценно использовать можно только simogeo и elFinder. В самописном шаблоне пришлось бы слишком много чего исправлять и дополнительно реализовывать.

  • Редактор simogeo это достаточно простой и приятный редактор, который не только умеет сам настраивать связь с CKEditor (через одну настройку CKEditor - url к менеджеру), но и достаточно легковесный. Simogeo написан с использованием jQuery, так что его код достаточно просто читать и, при необходимости, исправлять. И самое важное, у него уже реализован простой и понятный коннектор для Asp.Net (через IHttpHandler), в который легко встроиться (например, для учета настроек безопасности).

    Если рассматривать недостатки simogeo, то у него есть ряд небольших недочетов: директория с изображениями устанавливается на уровне js (т.е. теоретически любой имеющий доступ к редактору может задать путь так, как ему хочется), проблемы с расположением элементов управления (из-за чего изменение пути может приводить к кривизне других элементов управления), отсутствие нескольких фраз в локализации и ряд других мелких проблем. Однако, все эти недочеты легко исправить.

  • elFinder. Красивый и многофункциональный. Таким приятно было бы пользоваться. Однако, один единственный коннектор на Asp.Net для серверной части быстро изменил мнение в худшую сторону. Основной проблемой стала автоматизация, доведенная до предела.

    Во-первых, конфигурация задается только в web.config. Это сразу предвещает изменение кода. Готовые библиотеки можно и нужно отмести. Во-вторых. Открыв исходники, можно увидеть, что коннектор через чур усложнен. Использование IoC только для вызова нескольких разнородных сервисов, конечно, упрощает обработку и вызов выполняемых операций. Но, этот факт говорит о том, что вам придется подключать к своему проекту еще пару проектов (один из них достаточно увесистый). В противном случае, найти ошибку или сбой нереально.

    В-третьих. Конфигурация пронизывает сервисы. Что-то изменить - крайне не просто. А в случае если пути к папкам с файлами должны динамически меняться, то без полной переделки вам не обойтись. В-четвертых. Отсутствие нормальной возможности встроиться и отсутствие каких-либо механизмов безопасности, хотя бы собственных, говорят о серьезной проблеме. По сути, в большинстве случаев, зная адрес, абсолютно любой может отредактировать файлы на сервере. Это не только пугает, но и предвещает "приятные посиделки за чужим кодом". В-пятых. Взглянув на увесистость кода и способ вызова команд, можно смело сказать, что переписать, упростить и сделать коннектор elFinder более гибким - задача далеко не одной ночи.

Как итог, был выбран simogeo (скачать с оф. проекта). Хоть, по своим возможностям менеджер проигрывает elFinder и требует небольших "доделок". Somigeo все же реализует всю базовую функциональность, немного весит и позволяет легко встроиться.

 

Подключаем и настраиваем WYSIWYG редактор CKEditor в проект Asp.Net MVC 3

Первым делом добавляем всю папку с редактором в "Scripts/editor" и удаляем лишние папки (в зависимости от версии каталоги могут меняться, но для 3.6 - это _sample и _code). Стоит так же почистить каталог от лишних файлов, например, от php файлов и файлов локализации. 

Прикручиваем WYSIWYG редактор ckeditor c файловым менеджером simogeo в Asp.Net MVC 3

Примечание: Конечно, это не очень хорошо, когда все ресурсы (css и картинки) смешиваются со скриптами. Тем не менее, чтобы избежать возможных проблем с путями и последующих долгих рутинных исправлений, лучше всего хранить все в одной папке.

Теперь создадим небольшую страничку и внутри нее создадим textarea

<div class="material-body">
    <div class="material-text">
        <textarea class="material-inner-text"></textarea>
    </div>
</div>

Подключим необходимые js-файлы, включая адаптер для интеграции с jQuery.

 
<script type="text/javascript"
            src="/Scripts/editor/ckeditor/ckeditor.js"></script>
<script type="text/javascript"
            src="/Scripts/editor/ckeditor/adapters/jquery.js"></script>

Теперь напишем простой скрипт и убедимся, что CKEditor корректно подключен.

$(function () {
    $('.material-inner-text').ckeditor();
});

Вместо обычного текстового поля должен появится редактор:

Прикручиваем WYSIWYG редактор ckeditor c файловым менеджером simogeo в Asp.Net MVC 3

В принципе, на этом пока все.

Примечание: Все настройки, связанные с языком по умолчанию, а так же с отображением, вы можете настроить по своему желанию. В рамках данной статьи, они не рассматриваются. 

 

Подключаем и настраиваем файловый менеджер simogeo в проект Asp.Net MVC 3

Теперь пришло время подключить и настроить файловый менеджер simogeo. Точно так же добавляем всю папку в "Scripts/editor" и чистим от лишних файлов: удаляем все с расширением ".py" (так как питон мы не используем), удаляем "utils", удаляем папку "userprofiles" и оставляем в папке "connector" только каталог "ashx". Должно получится примерно так:

Прикручиваем WYSIWYG редактор ckeditor c файловым менеджером simogeo в Asp.Net MVC 3

Примечание: Можно и более тщательно почистить каталоги, но это лучше делать по мере использования, чтобы знать наверняка от чего можно избавиться. В любом случае, держите всегда под рукой исходные дистрибутивы, тогда всегда можно будет откатить изменения назад.

Теперь необходимо приступить к настройке менеджера. И начать нужно с конфигурации в специальном js файле. Для этого скопируйте или переименуйте файл "filemanager.config.js.default" в папке "simogeo/scripts/" в файл с названием "filemanager.config.js". И отредактируйте самое начало файла следующим образом (если по каким-то причинам настройки не применились, попробуйте продублировать изменения в "filemanager.config.js.default", так как иногда могут возникать проблемы с применением и загрузкой js):

"_comment": "IMPORTANT : go to the wiki page to know about options configuration github.com/simogeo/Filemanager/wiki/Filemanager-configuration-file",
"options": {
    "culture": "ru",
    "lang": "ashx",
    "defaultViewMode": "grid", 
.............
    "chars_only_latin": false,
.............
    "serverRoot": false,
    "fileRoot": "/",
.............
    "capabilities": ["select", "download", "rename", "delete"],

В данном случае интересуют несколько параметров

  • "culture", в котором был заменен "en" на "ru"
  • "lang", в котором необходимо изменить "php" на "ashx"
  • "chars_only_latin", в котором заменим значение на "false", чтобы воспринимался русский язык
  • "serverRoot", в котором значение было изменено на "false"
  • "fileRoot", в котором значение было изменено на '/' 
  • "capabilities", в котором нужно убрать "replace", так как данная возможность не реализована

Примечание: Внутри файла конфига достаточно много параметров. Вы можете настроить их по собственному желанию. Так как для подключения они не особо важны, другие настройки не упоминаются в статье.

Переходим к настройке обертки - файл "filemanager.ashx" в каталоге "Scripts/editor/simogeo/connectors/ashx". Начать стоит с одной настройки, которую необходимо изменить, а именно с пути к папке с иконками (параметр IconDirectory):

//===================================================================
//==================== EDIT CONFIGURE HERE ==========================
//===================================================================
public string IconDirectory = "/Scripts/editor/simogeo/images/fileicons/"; // Icon directory for filemanager. [string]
public string[] imgExtensions = new string[] { ".jpg", ".png", ".jpeg", ".gif", ".bmp" }; // Only allow this image extensions. [string]
//===================================================================
//========================== END EDIT ===============================
//===================================================================

К сожалению, управлением корнем директории с изображениями происходит на стороне JavaScript, что неправильно с точки зрения безопасности. По сути, та же проблема, что и с elFinder, только в более простом виде. Поэтому необходимо добавить еще один параметр в "filemanager.ashx", и так же изменить немного его наполнение.

//===================================================================
//==================== EDIT CONFIGURE HERE ==========================
//===================================================================
public string IconDirectory = "/Scripts/editor/simogeo/images/fileicons/"; // Icon directory for filemanager. [string]
public string StorageDirectory = "/Storage/Img/";
public string[] imgExtensions = new string[] { ".jpg", ".png", ".jpeg", ".gif", ".bmp" }; // Only allow this image extensions. [string]
//===================================================================
//========================== END EDIT ===============================
//===================================================================

Добавим функцию для обрезания путей хранения, чтобы путь до хранилища не мешался в менеджере на клиентской стороне.

#region Убираем реальный путь из клиентского менеджера
 
private string RemoveRootBeforeFormResponse(string path)
{
    return path.StartsWith(this.StorageDirectory)
        ? path.Remove(0, this.StorageDirectory.Length) : path;
}
 
#endregion

Изменим функцию ProcessRequest (добавим ко всем путям StorageDirectory) и немного преобразим код:

 
public void ProcessRequest (HttpContext context) 
{
    context.Response.ClearHeaders();
    context.Response.ClearContent();
    context.Response.Clear();
 
    context.Response.ContentType = "plain/text";
    context.Response.ContentEncoding = Encoding.UTF8;
 
    string path = this.StorageDirectory + (context.Request["path"] ?? ""),
            oldPath = this.StorageDirectory + (context.Request["old"] ?? ""),
            currentPath = this.StorageDirectory + (context.Request["currentpath"] ?? "");
 
    switch (context.Request["mode"])
    {
        case "getinfo":
            context.Response.Write(getInfo(path));
            break;
        case "getfolder":
             context.Response.Write(getFolderInfo(path));
            break;
        case "rename":
            context.Response.Write(Rename(oldPath, context.Request["new"]));
            break;
        case "delete":
            context.Response.Write(Delete(path));
            break;
        case "addfolder":
            context.Response.Write(AddFolder(path, context.Request["name"]));
            break;
        case "download":
            FileInfo fi = new FileInfo(context.Server.MapPath(path));
            context.Response.AddHeader("Content-Disposition", "attachment; filename=" + context.Server.UrlPathEncode(fi.Name));
            context.Response.AddHeader("Content-Length", fi.Length.ToString());
            context.Response.ContentType = "application/octet-stream";
            context.Response.TransmitFile(fi.FullName);
            break;
        case "add":
            System.Web.HttpPostedFile file = context.Request.Files[0];
            file.SaveAs(context.Server.MapPath(Path.Combine(currentPath, Path.GetFileName(file.FileName))));
            context.Response.ContentType = "text/html";
 
            StringBuilder sb = new StringBuilder();
            
            sb.AppendLine("{");
            sb.AppendLine("\"Path\": \"" + RemoveRootBeforeFormResponse(currentPath) + "\",");
            sb.AppendLine("\"Name\": \"" + Path.GetFileName(file.FileName) + "\",");
            sb.AppendLine("\"Error\": \"No error\",");
            sb.AppendLine("\"Code\": 0");
            sb.AppendLine("}");
 
            System.Web.UI.WebControls.TextBox txt = new System.Web.UI.WebControls.TextBox();
            txt.TextMode = System.Web.UI.WebControls.TextBoxMode.MultiLine;
            txt.Text = sb.ToString();
            StringWriter sw = new StringWriter();
            System.Web.UI.HtmlTextWriter writer = new System.Web.UI.HtmlTextWriter(sw);
            txt.RenderControl(writer);
            context.Response.Write(sw.ToString()); 
            
            break;
        default:
            break;
    }
}

Теперь необходимо пройтись по всем функциям, чтобы исправить возвращаемый адрес.

private string getFolderInfo(string path)
{
    //..............................
    foreach (DirectoryInfo DirInfo in RootDirInfo.GetDirectories()) 
    {
        //..............................
        sb.AppendLine("\"" + Path.Combine(RemoveRootBeforeFormResponse(path), DirInfo.Name) + "\": {");
        sb.AppendLine("\"Path\": \"" + Path.Combine(RemoveRootBeforeFormResponse(path), DirInfo.Name) + "/\",");
        //..............................
    }
    foreach (FileInfo fileInfo in RootDirInfo.GetFiles())
    {
        //..............................
        sb.AppendLine("\"" + Path.Combine(RemoveRootBeforeFormResponse(path), fileInfo.Name) + "\": {");
        sb.AppendLine("\"Path\": \"" + Path.Combine(RemoveRootBeforeFormResponse(path), fileInfo.Name) + "\",");
        //..............................
    }
    //..............................
}
 
private string getInfo(string path) 
{
    //..............................
    if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
    {
        //..............................
        sb.AppendLine("\"Path\": \"" + RemoveRootBeforeFormResponse(path) + "\",");
        //..............................
    }
    else
    {
        //..............................
        sb.AppendLine("\"Path\": \"" + RemoveRootBeforeFormResponse(path) + "\",");
        //..............................
    }
    //..............................
}
 
private string Rename(string path, string newName)
{
    //..............................
    if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
    {
        //..............................
        sb.AppendLine("\"Old Path\": \"" + RemoveRootBeforeFormResponse(path) + "\",");
        //..............................
    }
    else 
    {
        //..............................
        sb.AppendLine("\"Old Path\": \"" + RemoveRootBeforeFormResponse(path) + "\",");
        //..............................
    }
    //..............................
}
 
private string Delete(string path) 
{
    //..............................
    sb.AppendLine("\"Path\": \"" + RemoveRootBeforeFormResponse(path) + "\"");
    //..............................
}
 
private string AddFolder(string path, string NewFolder)
{
    //..............................
    sb.AppendLine("\"Parent\": \"" + RemoveRootBeforeFormResponse(path) + "\",");
    //..............................
}

Примечание: Конечно, хорошо бы было еще убрать код из ashx и спрятать его во внутренних классах, чтобы исходники не оставались в открытом виде, но это вы можете сделать самостоятельно.  

Теперь необходимо связать CKEditor с simogeo. Для этого чуть исправим скрипт для запуска редактора:

$(function () {
    $('.material-inner-text').ckeditor({
        filebrowserBrowseUrl: '/Scripts/editor/simogeo/index.html?type="Images"'
    });
});

Однако, в связи с тем, что был изменен механизм путей (перенесен на серверную часть), необходимо подправить код менеджера, чтобы корректно вставлялся url. Для этого вначале поменяем в index.html строку загрузки "filemanager.min.js" на "filemanager.js"

<script type="text/javascript" src="/scripts/filemanager.js"></script>

И добавим в функцию "getInfo" в файле"filemanager.ashx" вывод реального параметра:

private string getInfo(string path) 
{
    //...........................
    else
    {
        FileInfo fileInfo = new FileInfo(HttpContext.Current.Server.MapPath(path));
 
        sb.AppendLine("{");
        sb.AppendLine("\"Path\": \"" + RemoveRootBeforeFormResponse(path) + "\",");
        // Добавляемая строка
        sb.AppendLine("\"RealPath\": \"" + path + "\",");
        sb.AppendLine("\"Filename\": \"" + fileInfo.Name + "\",");

После чего корректируем сам "filemanager.js". А именно немного изменяем функцию обработки выбранного элемента "selectItem". Код необходимо изменить так, чтобы при выборе использовался реальный путь. И делается это следующим образом:

var selectItem = function(data) {
    if(config.options.relPath !== false && typeof (config.options.relPath) === 'string') {
        //var url = relPath + data['Path'].replace(fileRoot,""); 
        // Заменяем на 
        var url = config.options.relPath + data['RealPath'].replace(fileRoot,"");
    } else {
        //var url = relPath + data['Path'];
        // Заменяем на
        var url = relPath + data['RealPath'];
    }

Остался последний штрих. Исправляем файл локализации "ru.js" в каталоге "/Scripts/editor/simogeo/scripts/languages"

//"new_folder": "New folder",
// Заменяем на 
"new_folder": "Создать каталог",

Примечание: Конечно, в данном коде можно найти еще ряд вещей, которые можно и нужно исправить. Например, отсутствие обработки исключений внутри IHttpHandler. Однако, эту и другие проблемы легко исправляются по мере встраивания в обертку. Или же вы можете оставить все так, как настроено. 

 

Смотрим на результат WYSIWYG редактор ckeditor с файловым менеджером для проекта Asp.Net MVC 3

Прикручиваем WYSIWYG редактор ckeditor c файловым менеджером simogeo в Asp.Net MVC 3

После всех исправлений, у вас есть готовая связка "WYSIWYG редактор CKEditor" + "файловый менеджер simogeo" + "Asp.Net MVC 3". 

Примечание: Возможно, вы ожидали увидеть ссылку на тестовый проект. Однако, ее тут нет. И сделано это умышленно. Так как в статье исправляются ресурсы проектов, то вам просто необходимо хоть раз пройтись по инструкции, чтобы понимать зачем делались те или иные исправления. В противном случае, в будущем, при попытке что-то изменить или исправить, вы можете "неожиданно" натолкнуться на проблемы (и скорее всего так и будет) в связи с изменением логики поведения кода.

☕ Понравился обзор? Поделитесь с друзьями!

Добавить комментарий / отзыв

Комментарий - это вежливое и наполненное смыслом сообщение (правила).



* Нажимая на кнопку "Отправить", Вы соглашаетесь с политикой конфиденциальности.
Присоединяйтесь
 

 

Программы (Freeware, OpenSource...)