Посты / Простой, легкий и мощный WYSIWYG текстовый редактор для Laravel - Summernote

14.02.2017 20:15
Привет друзья. Я хотел бы сегодня затронуть одну тему, которая касается интеграции WYSIWYG текстового редактора во фрэймворк Laravel. В принципе это касается не только Ларавел, но и любого другого фреймворка или CMS. Но в этой статье я все же приведу пример для Ларавел 5.4

Как только заходит речь о WYSIWYG редакторе, то у всех сразу всплывает из подсознания два таких монстра, как CKEditor и TinyMVC. Бесспорно - это отличнейшие проекты, которые имеют громадный функционал. Но также они имеют и громадный размер.

А еще встречается проблема, что прикрутить их к тому или иному WEB проекту не всегда тривиальная задача. Огромные дистрибутивы, множество файлов настроек, приличный пакет документации - это только один из моментов, с которым вы столкнетесь при установке этих решений.

Но давайте себе ответим на один вопрос. А что нам нужно от текстового редактора? Я буду загибать свои пальцы:

  • возможность работать всегда и везде
  • очень маленький размер дистрибутива
  • минимальный набор необходимого функционала
  • быстрая скорость загрузки
  • элементарная интеграция в любое текстовое поле
  • возможность простой и быстрой настройки под свои нужды
  • красивая загрузка файлов в сам редактор (файл при этом можно сохранить на диске)
  • красивое удаление файлов из редактора (файл при этом можно удалить с диска)
  • возможность написать свой плагин (легко и быстро)

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

Я же могу предложить, для большинства разработчиков и пользователей, более простое и гибкое решение. Простой редактор, созданный на Bootstrap, который весит всего 500Кб - Summernote. И это вместе с плагинами и файлами локализации!

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

Но самое главное - это то, что на сохранение и удаление изображений можно легко навесить свои обработчики. А это означает, что вы можете легко и не принуждено организовать систему автозагрузки и автоудаления изображений на диске.

Давайте создадим в resources/assets/js/ файл и назовем его, к примеру, summernote_editor_settings.js

Внутри напишем такой код:

/**
* Settings for summernote editor.
*/

$(document).ready(function () {

$(document).ready(function () {

var editor = $('#editor-body');

var configFull = {
lang: 'ru-RU', // default: 'en-US'
shortcuts: false,
airMode: false,
minHeight: null, // set minimum height of editor
maxHeight: null, // set maximum height of editor
focus: false, // set focus to editable area after initializing summernote
disableDragAndDrop: false,
callbacks: {
onImageUpload: function (files) {
uploadFile(files);
},

onMediaDelete: function ($target, editor, $editable) {

var fileURL = $target[0].src;
deleteFile(fileURL);

// remove element in editor
$target.remove();
}
}
};

// Featured editor
editor.summernote(configFull);

// Upload file on the server.
function uploadFile(filesForm) {
data = new FormData();

// Add all files from form to array.
for (var i = 0; i < filesForm.length; i++) {
data.append("files[]", filesForm[i]);
}

$.ajax({
data: data,
type: "POST",
url: "/ajax/uploader/upload",
cache: false,
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
contentType: false,
processData: false,
success: function (images) {
//console.log(images);

// If not errors.
if (typeof images['error'] == 'undefined') {

// Get all images and insert to editor.
for (var i = 0; i < images['url'].length; i++) {

editor.summernote('insertImage', images['url'][i], function ($image) {
//$image.css('width', $image.width() / 3);
//$image.attr('data-filename', 'retriever')
});
}
}
else {
// Get user's browser language.
var userLang = navigator.language || navigator.userLanguage;

if (userLang == 'ru-RU') {
var error = 'Ошибка, не могу загрузить файл! Пожалуйста, проверьте файл или ссылку. ' +
'Изображение должно быть не менее 500px!';
}
else {
var error = 'Error, can\'t upload file! Please check file or URL. Image should be more then 500px!';
}

alert(error);
}
}
});
}

// Delete file from the server.
function deleteFile(file) {
data = new FormData();
data.append("file", file);
$.ajax({
data: data,
type: "POST",
url: "/ajax/uploader/delete",
cache: false,
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
contentType: false,
processData: false,
success: function (image) {
//console.log(image);
}
});
}

});

});
В принципе из кода понятно, что редактор будет цепляться на элемент с id="editor-body". Это должен быть textarea тэг:
<div class="form-group">
<div class="form-group{!! $errors->has('body') ? ' has-error' : '' !!}">
{!! Form::label('body', 'Body' . ':', ['class' => 'control-label']) !!}
{!! Form::textarea('body', NULL, ['class' => 'form-control', 'id' => 'editor-body']) !!}

@if ($errors->has('body'))
<span class="help-block">
<strong>{!! $errors->first('body') !!}</strong>
</span>
@endif
</div>
</div>

Что касается обработчиков, то они уже настроены на работу пакета для управления изображениями UploadImage.

Нужно не забыть добавить наш файл настроек в gulp файл (исхожу из того, что elixir laravel у вас подключен):
// Copy editor settings for summernote.
mix.copy('resources/assets/js/summernote_editor_settings.js', 'public/js/summernote_editor_settings.js');
В данном конкретном случае я просто скопировал файл. Также стоит не забывать, что у вас должен быть подключен Bootstrap:
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js"></script>
Подключать его нужно в главном шаблоне: app.blade.php

Сам же дистрибутив Summernote нужно разместить в папке public, например в public/modules/summernote

Теперь в шаблоне формы, где будет ваш редактор, нужно подключить три файла: сам редактор, файл локализации и файл с вашими настройками.
{{-- Add wysiwyg script --}}
<script src="{!! asset('modules/summernote/summernote.js') !!}"></script>

{{-- Include summernote-ru-RU --}}
<script src="{!! asset('modules/summernote/lang/summernote-ru-RU.js') !!}"></script>

{{-- Include editor settings file for users --}}
<script src="{!! asset('js/summernote_editor_settings.js') !!}"></script>
Вот и все! В итоге вы получите возможность не просто добавлять тексты и форматировать их, но и легко добавлять изображения в ваш текст, не заботясь о том, как их загрузить на сервер, а потом удалить. Все будет происходить автоматически! При этом, за раз мы можете выделить сразу десять файлов и они одним махом вставятся в редактор.

Легко, просто и гибко. До новой встречи. Всем удачи.
1

Комментарии (17):

pupkins писал(а):
Добрейшего Вам дня/вечера/ночи! Очередная проблема поставила меня в тупик. Установка параметра options в "maximumImageFileSize: null" ничего не меняет, ограничения по размеру файла всё равно остаются до 3 мегабайт. Это только где-то в summernote, на сервере и в форме ограничений нет. Вы с таким не сталкивались?
Салют. Не уверен, что null корректное значение. Вот тут есть пример, https://github.com/summernote/summernote/issues/2307

Продублирую здесь:

If found the solution:

$(this).summernote({
  maximumImageFileSize: 524288 # 512KB
});

PS: to caluclate the number, you need to multiply 1024. Example:

1024 * 512KB = 524288
Добрейшего Вам дня/вечера/ночи! Очередная проблема поставила меня в тупик. Установка параметра options в "maximumImageFileSize: null" ничего не меняет, ограничения по размеру файла всё равно остаются до 3 мегабайт. Это только где-то в summernote, на сервере и в форме ограничений нет. Вы с таким не сталкивались?
Anque писал(а):
пробую установить этот редактор (elixir'a, вроде бы, нету. ставил по дефолтному гайду - https://laravel.ru/docs/v5/installation, Windows). при выводе формы получаю такое: "(1/1) FatalErrorExceptionClass 'Form' not found in b2fc94541818c0b6867b9efee56039f0e07fce51.php (line 60) "
А можете чуть подробнее рассказать, о чем речь идет? Что вы хотите сделать и как вы это делали.
пробую установить этот редактор (elixir'a, вроде бы, нету. ставил по дефолтному гайду - https://laravel.ru/docs/v5/installation, Windows).

при выводе формы получаю такое:
  "(1/1) FatalErrorExceptionClass 'Form' not found

in b2fc94541818c0b6867b9efee56039f0e07fce51.php (line 60) " 
pupkins wrote:
буду обращаться с вопросами, если Вы не против. Спасибо, сегодня Вы спасли меня!
Пожалуйста. Обращайтесь :)
Спасибо, это работает прекрасно! Буду сейчас прикручивать. И удивительно, что народ годами пытается решить задачу в своем редакторе и не решает ее. Находится русский программист и все решено за час. 
Добавьтесь на страницу https://github.com/summernote/awesome-summernote#plugins
Чувствую, это начало доработки очень неплохого, но заброшенного редактора. Я выбираю его для своего монументального творения - конструктора порталов, будет много сложностей, буду обращаться с вопросами, если Вы не против. Спасибо, сегодня Вы спасли меня!
pupkins wrote:
В самом конце ветви https://github.com/summernote/summernote/issues/1203 Правда, там обсуждается громоздкий elfinder как средство, что не приемлемо, учитывая легкость (пока кажущуюся) реализации этого в самом редакторе. Безусловно, не нужна никакая навигация по серверу, - лишь загрузка, ну в крайнем случае удаление (прекрасно реализованное здесь в этом посте).
Привет. Вот выкроил пару часов и написал простой плагин. Надеюсь - это вам поможет: https://github.com/kirill-dan/uploadfile Будут вопросы - пишите. Постараюсь ответить.
Я чуть разгружусь, могу сделать такую формочку. Это не проблема.
Вид popup для плагина может быть таким:

В самом конце ветви https://github.com/summernote/summernote/issues/1203
Правда, там обсуждается  громоздкий elfinder как средство, что не приемлемо, учитывая легкость (пока кажущуюся) реализации этого в самом редакторе. Безусловно, не нужна никакая навигация по серверу, - лишь загрузка, ну в крайнем случае удаление (прекрасно реализованное здесь в этом посте). 

pupkins wrote:
посмотрим, куда он вставит мой текст...
Должен вставить в то место, где был фокус курсора.
Спасибо, это просто, посмотрим, куда он вставит мой текст...
pupkins wrote:
Перехват файла ясен, обработчик тоже, у меня возникли трудности с возвращением ссылки на файл в поле редактора (всего-то!).
Вот ссылка на их АПИ: https://summernote.org/deep-dive/#insertion-api. Нужно получить путь к файлу, который вы сохраните на бэке. Вы ведь знаете куда его сохраняете. А в колбэк аякса вернуть всего-навсего простую текстовую ссылку: https://mysite.com/myfile.txt 

После чего вставить в виде текста в редактор, например так: 
// @param {String} text
$('#summernote').summernote('insertText', '<a href="' + myLink + '">' + 'Download file</a>');
Как-то так. Я просто сейчас убегаю, буду только вечером. Не получится, подскажу :)
Безусловно устроит то что Вы предложили! В ларавел я лично не работаю, но думаю, что это очень важно тоже, главное - реализовать это просто. Перехват файла ясен, обработчик тоже, у меня возникли трудности с возвращением ссылки на файл в поле редактора (всего-то!). Может, совместно мы это сделаем сегодня?
pupkins wrote:
О, если бы Вы смогли реализовать это! Не только я, но и сообщество, которое пытается это сделать до сих пор, с должным достоинством и уважением восприняли бы этот плагин.
А вы с php хорошо знакомы? Тут ведь по сути смысл в том, чтобы перехватить файл из формы и передать его аяксом на бэкенд. Бэкенд (php или другой язык) его проверят, сохранят и возвращает назад в плагин ссылку на файл, которую потом вставит плагин в редактор. В принципе я могу это сделать, но не прямо сейчас. У меня сейчас дурдом, освобожусь только через пару недель. Сам плагин могу сделать, могу даже еще написать бэкенд сторону для Ларавел, если вас это устроит.
Спасибо за быстрый ответ. Я действительно , являясь не лучшим специалистом в яваскрипте, имею тщетные попытки, используя загрузку картинки, реализовать загрузку файла. О, если бы Вы смогли реализовать это! Не только я, но и сообщество, которое пытается это сделать до сих пор, с должным достоинством и уважением восприняли бы этот плагин.
pupkins wrote:
Вы не пробовали реализовать загрузку не картинок а файлов по той-же схеме с отображением в итоге простой ссылки на скачивание? Очень актуальная проблема для меня сейчас. Да и как посмотрю, разработчик тоже ничего не предпринял в этом направлении по сей день. Только полумеры.
Честно говоря у меня не было такой необходимости, но можно сделать к редактору плагин. Я делал плагин для автоматического создания демотиватора на ходу, можно его переделать в загрузку файла. Тут в принципе нет ничего сложного. По сути, ведь изображение - это тот же файл. Вопрос только в том, чтобы вам вместо файла не залили какой-то эксплойт (вредоносный код), который даст злоумышленнику доступ к вашему серверу. А значит, нужно со стороны бэкенда делать какой-то анализатор загружаемого файла.
Вы не пробовали реализовать загрузку не картинок а файлов по той-же схеме с отображением в итоге простой ссылки на скачивание? Очень актуальная проблема для меня сейчас. Да и как посмотрю, разработчик тоже ничего не предпринял в этом направлении по сей день. Только полумеры.