Так получилось что прикрутить один визивиг недостаточно, нужна по-любому загрузка картинок. Визуально лучшим редактором и более подходящим по возможностям мы выбрали CKEditor 5 в режиме balloon block. Чтобы было понятнее, режим balloon block это блочное редактирование прямо на странице с материалом.
Но есть проблема, конфиг прошлой версии CKEditor 4 не подходит. Вторая проблема, конфиг MyCustomUploadAdapter, который достаточно просто найти в сети, не работает. Перепробовал несколько версий, то идут версии чисто под реакт, то нерабочая, отправляющая вместо файла на сервер Promise.
В итоге, использовал тот же MyCustomUploadAdapter из сети с поправой из свежей версии CKFinder адаптера.
Сразу обозначу что код рабочий, но не чистый и добавлял его чтобы не забыть что как делал, но и возможно кому-то пригодиться. Кому нужно, код поправят под то как им нужно. Все это черновой код и продакшн код я не публикую специально. В итоге код выглядит так:
class MyUploadAdapter {
constructor( loader, url, t ) {
// CKEditor 5's FileLoader instance.
this.loader = loader;
// URL where to send files.
this.url = '{{route('front_uploader')}}';
//translate function
//this.t = t;
}
// Starts the upload process.
upload() {
return this.loader.file.then( file => {
return new Promise( ( resolve, reject ) => {
this._initRequest();
this._initListeners( resolve, reject, file );
this._sendRequest( file );
} );
} );
}
// Aborts the upload process.
abort() {
if ( this.xhr ) {
this.xhr.abort();
}
}
// Example implementation using XMLHttpRequest.
_initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open( 'POST', this.url, true );
xhr.responseType = 'json';
}
// Initializes XMLHttpRequest listeners.
_initListeners( resolve, reject, file ) {
console.log(file);
const xhr = this.xhr;
const loader = this.loader;
//const t = this.t;
const genericError = ( 'Cannot upload file:' + ` ${ file.name }.`);
xhr.addEventListener( 'error', () => reject( genericError ) );
xhr.addEventListener( 'abort', () => reject() );
xhr.addEventListener( 'load', () => {
const response = xhr.response;
if ( !response || !response.uploaded ) {
console.log(response);
return reject( response && response.error && response.error.message ? response.error.message : genericError );
}
resolve( {
default: response.url
} );
} );
// Upload progress when it's supported.
/* istanbul ignore else */
if ( xhr.upload ) {
xhr.upload.addEventListener( 'progress', evt => {
if ( evt.lengthComputable ) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
} );
}
}
/**
* Prepares the data and sends the request.
*
* @private
* @param {File} file File instance to be uploaded.
*/
_sendRequest( file ) {
// Prepare form data.
const data = new FormData();
data.append( 'upload', file );
//data.append( 'ckCsrfToken', getCsrfToken() ); //это для тех кому важна csrf от CKEditor/CKFinder
// Send request.
this.xhr.send( data );
}
}
document.addEventListener('DOMContentLoaded',function (){
function MyCustomUploadAdapterPlugin( editor ) {
editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
return new MyUploadAdapter( loader );
};
}
const config = {
extraPlugins: [ MyCustomUploadAdapterPlugin ],
simpleUpload: {
uploadUrl: '/uploader' //путь куда сохраняется материал
}
};
var ta = document.querySelector( '.textarea' );
BalloonEditor
.create( ta, config
).then( editor => { window.editor = editor } )
.catch( error => {
console.error( error );
} );
//код сохранения по клику на кнопку
document.getElementById( 'saveArticle' ).onclick = () => {
$.post('{{route('admin_articles_edit',['id'=>$record->id])}}',{ //роут сохранения статьи
'fields[body]':editor.getData(),
_token: '{{ csrf_token() }}' //csrf от Laravel
})
}
}); Бекенд на ларавеле отрабатывает приблизительно так:
<?php namespace App\Http\Controllers\Front;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
class Fileuploader extends Controller {
public function upload(Request $request){
if (Auth::guest()) abort(403);
$file = $request->file('upload');
if ($file->isReadable()){
$filename = md5(date("YmdHis").rand(5,50));
$extension = $file->getClientOriginalExtension();
Storage::disk('front_uploaded')->put($filename.'.'.$extension, File::get($file));
$http_path = '/images/uploaded/front/'.$filename.".".$extension;
return ['url'=>$http_path,'file'=>['name'=>$filename,'url'=>$http_path],'uploaded'=>true];
}
else
{
return ['failed'];
}
}
}Не забудьте путь добавить в исключение VerifyCsrfToken для Laravel или прикрутить нормально проверку:
protected $except = [
'uploader',
];и сам путь добавить в роутинг:
Route::group(['prefix' => 'uploader', 'nocsrf' => true], function () {
Route::match(['get', 'post'], '/', 'Fileuploader@upload')->name('front_uploader');
});Протестированно на внутренних проектах https://100.ks.ua и отработало нормально