Давно не писал в блог, был занят разработкой функционала для публикации статей из frontend для WordPress на одном из своих сайтов. В моменте столкнулся с проблемой добавления изображений через кнопку на панели инструментов классического редактора TinyMCE в WordPress. Два дня убил на решение этой задачи, перерыл много сайтов в итоге ничего не нашел и пришлось делать все на своём опыте, ниже дам готовый код и инструкцию, возможно кому-то пригодится.
1 Шаг. Добавляем кнопку в панель инструментов
Ниже код функции wp_editor, которая отвечает за вывод редактора. Я указал для toolbar1 набор инструментов, которые как мне показалось наиболее нужные. Обратите внимание, что в этом списке есть элемент image, это плагин редактора TinyMCE, который отвечает за вывод кнопки для добавления изображений в редактор.
wp_editor( '', 'editor', array(
'wpautop' => 1,
'media_buttons' => 0,
'textarea_name' => 'content', //нужно указывать!
'textarea_rows' => 20,
'tabindex' => null,
'editor_css' => '',
'editor_class' => '',
'teeny' => 0,
'dfw' => 0,
'tinymce' => [
'toolbar1' => 'formatselect,bold,italic,underline,strikethrough,bullist,numlist,alignleft,aligncenter,alignright,image,blockquote,link,unlink,fullscreen,spellchecker',
'toolbar2' => '',
'toolbar3' => '',
],
'quicktags' => 0,
'drag_drop_upload' => false
) ); P.S. Функцию вставляете туда, где у вас должен выводиться редактор. По итогу в вас будет выводится такая панель в редакторе

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

2 Шаг. Улучшаем форму добавления изображений
Это самая сложная часть, которая заставила меня 2 дня штурмовать поиск, где по итогу я ничего толкового не нашёл и весь код писал сам, взяв за основу лишь некоторые куски js кода из документации TinyMCE.
В общем, этот код вставляем в functions.php, как видите мы будем цепляться к фильтру tiny_mce_before_init и в своей функции будем расширять функционал плагина image для TinyMCE, чтобы пользователи могли выбирать изображение со своего компьютера.
В коде есть некоторые пояснения, обязательно обратите внимание на строку 27, там нужно указать класс вашей формы, чтобы в неё добавлялись поля input с типом file для их отправки вместе с формой для дальнейшей обработки изображений на сервере.
add_filter( 'tiny_mce_before_init', 'wpgid_tiny_mce_before_init_filter', 10, 2 );
function wpgid_tiny_mce_before_init_filter( $mceInit, $editor_id ){
$mceInit['image_title'] = true;
$mceInit['automatic_uploads'] = false;
$mceInit['file_picker_types'] = 'image';
$mceInit['file_picker_callback'] = "function (cb, value, meta) {
if(!value) {
// Если вставляем новое изображение, то создаем input с уникальным атрибутом name
var fileInput = document.createElement('input');
fileInput.setAttribute('type', 'file');
fileInput.setAttribute('accept', 'image/*');
fileInput.setAttribute('style', 'display:none');
fileInput.setAttribute('class', 'tinymce-image');
fileInput.onchange = function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
var id = 'blobid' + (new Date()).getTime();
var blobCache = tinymce.activeEditor.editorUpload.blobCache;
var base64 = reader.result.split(',')[1];
var blobInfo = blobCache.create(id, file, base64);
blobCache.add(blobInfo);
cb(blobInfo.blobUri(), { title: file.name });
// присваиваем атрибут name
document.querySelector('.post-editor').append(fileInput); // Замените класс на класс вашей формы
fileInput.setAttribute('name', id);
sessionStorage.setItem('editImageId', id);
};
reader.readAsDataURL(file);
};
fileInput.click();
} else {
// Если редактируем изображение
var imageId = sessionStorage.getItem('editImageId')
fileInput = getElementsByAttributeName('input', 'name', imageId)[0]
fileInput.onchange = function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
var id = 'blobid' + (new Date()).getTime();
var blobCache = tinymce.activeEditor.editorUpload.blobCache;
var base64 = reader.result.split(',')[1];
var blobInfo = blobCache.create(id, file, base64);
blobCache.add(blobInfo);
cb(blobInfo.blobUri(), { title: file.name });
};
reader.readAsDataURL(file);
};
fileInput.click();
}
// Вспомогательная функция для поиска по атрибуту с нужным значением
function getElementsByAttributeName(tagName, attributeName, attributeValue) {
var i, n, objs=[], els=document.getElementsByTagName(tagName), len=els.length;
for (i=0; i<len; i++) {
n = els[i][attributeName];
if (n && (n==attributeValue)) {
objs.push(els[i]);
}
}
return objs;
}
}";
$mceInit['init_instance_callback'] = "function (editor) {
editor.on('NodeChange', function (e) {
if( e.element.nodeName == 'IMG' ) {
if( ! e.element.getAttribute('data-id') ) {
// Для новых изображений присваиваем атрибут data-id
e.element.setAttribute('data-id', sessionStorage.getItem('editImageId'));
} else {
// для редактируемых записываем в sessionStorage id редактируемой картинки
sessionStorage.setItem('editImageId', e.element.getAttribute('data-id'));
}
}
// В случае удаления картинки из редактора, удаляем input этой картинки
var images = e.element.offsetParent.getElementsByTagName('img');
var files = document.querySelectorAll('.tinymce-image');
var imagesIdArray = [];
var filesIdArray = [];
var deleteIds = [];
if( images.length > 0 ) {
for (var i = 0; i < images.length; i++) {
imagesIdArray.push(images[i].getAttribute('data-id'));
}
}
if( files.length > images.length && files.length > 0 ) {
for (var i = 0; i < files.length; i++) {
filesIdArray.push(files[i].getAttribute('name'));
}
deleteIds = filesIdArray.filter(x => !imagesIdArray.includes(x));
}
if( deleteIds.length > 0) {
for (var i = 0; i < deleteIds.length; i++) {
var file = getElementsByAttributeName('input', 'name', deleteIds[i])[0]
file.remove()
}
}
});
// Вспомогательная функция для поиска по атрибуту с нужным значением
function getElementsByAttributeName(tagName, attributeName, attributeValue) {
var i, n, objs=[], els=document.getElementsByTagName(tagName), len=els.length;
for (i=0; i<len; i++) {
n = els[i][attributeName];
if (n && (n==attributeValue)) {
objs.push(els[i]);
}
}
return objs;
}
}";
return $mceInit;
} В итоге у нас получается такая форма с кнопкой выбор файла с компьютера, что гораздо удобней чем стандартный вариант. После отправки формы, на сервере вам уже нужно обработать изображения и загрузить их в библиотеку используя переменную $_FILES.

P.S. Такой вариант загрузки изображений хорош тем, что изображения загружаются, только вместе с отправкой формы, а не в момент работы с редактором, при этом пользователь видит изображения и может полноценно с ними работать. Надеюсь этот код поможет вам. Если есть мысли как улучшить код, то пишите в комментариях и если будете перепечатывать статью на своем сайте, оставьте обратную ссылку на мой блог.
А размер изображения и формат настраиваются? Или работают настройки вордпресс?
На шаге 2 можно указать форматы файлов, которые можно выбирать пользователю для загрузки (строка 11)
Проверку размера файлов прямо в браузере можно дописать через JS и выдавать пользователю сообщение, если размер превышает допустимый. Я такой функционал у себя не закладывал.
Но в любом случае вам нужно будет на серверной части делать дополнительную проверку формата и размера файлов.