Загрузка файлов с помощью AJAX для вашего WordPress плагина
Загрузка файлов для плагина WordPress — не самая простая задача. Необходимо предоставить пользователю простой и понятный интерфейс, а также правильно обрабатывать загружаемые файлы, чтобы не создавать уязвимости.
Для того, чтобы не писать полностью собственный загрузчик файлов, мы будем использовать функционал WordPress, а именно файл async-upload.php, расположенный в папке wp-admin.
Используя этот файл мы получаем ряд преимуществ. Во-первых это стандартный файл для загрузки медиа файлов в WordPress, таким образом мы можем быть уверены в том, что код работает правильно. Так же в этом файле проводятся все необходимые проверки прав доступа и нам не придется делать это самостоятельно.
Требования
Чтобы использовать файл async-upload.php нужно следовать следующим правилам.
- Атрибут name поля для загрузки файла должен быть async-upload
- Защитный ключ, который мы отправляем в AJAX запросе, должен использовать стандартное имя _wpnonce, а значение его — результат работы функции wp_create_nonce(‘media-form’)
- В AJAX запросе мы должны отправить ключ action со значением upload-attachment, таким образом будет вызвана нужная нам функция wp_ajax_upload_attachment
Если мы будем следовать этим правилам, WordPress сможет корректно обработать наш AJAX запрос.
Плагин
Лучший способ продемонстрировать возможность загрузки файлов в WordPress — создать плагин. Мы создадим простой плагин, который позволяет зарегистрированным пользователям загружать изображения.
Так как цель этой статьи — демонстрация загрузки файлов с помощью AJAX, функционал плагина будет довольно скромным, наш плагин будет:
- Позволять администратору вставлять форму загрузки в любую страницу, используя шорткод.
- Показывать зарегистрированным пользователям форму загрузки файла с помощью AJAX.
- Отправлять уведомление о загрузке администратору.
Что можно добавить в плагин:
- Хранить информацию о загрузках в базе данных.
- Показывать список файлов в интерфейсе админа.
- Позволять не зарегистрированным пользователям загружать.
Создаем плагин
Как обычно, для создания нового WordPress плагина — создайте папку в директории wp-content/plugins. Будем использовать имя my-upload-plugin и префикс mup_ для всех собственных функций.
Далее, создайте главный файл плагина с таким же именем, как у папки. Также создадим папку js, в которой будет храниться файл script.js.
Должна получиться следующая структура:
wp-content/
|-- plugins/
|-- my-upload-plugin/
|-- js/
| |-- script.js
|--my-upload-plugin.php
Напишем заголовок плагина в главном файле my-upload-plugin.php, осле этого можно перейти на страницу с плагинами и активировать его.
<?php /* Plugin Name: Simple Uploader Plugin URI: http://easy-code.ru Description: Демонстрация AJAX загрузки в WordPress Version: 0.1.0 Author: Author Name Author URI: http://easy-code.ru */
Подключение скрипта
Чтобы использовать script.js, необходимо подключить скрипт:
function mup_load_scripts() {
wp_enqueue_script('image-form-js', plugin_dir_url( __FILE__ ) . 'js/script.js', array('jquery'), '0.1.0', true);
}
add_action('wp_enqueue_scripts', 'mup_load_scripts');
Нам нужно будет «локализовать» этот скрипт, так как в нем используются данные, которые могут меняться в зависимости от конкретной сессии. Для этого мы будем использовать функцию wp_localize_script(). Нам нужно изменить три переменные: ссылки на файлы admin-ajax.php и async-upload.php, а также защитный ключ, который генерирует функция wp_create_nonce().
Теперь код для подключения скрипта выглядит так:
function mup_load_scripts() {
wp_enqueue_script('image-form-js', plugin_dir_url( __FILE__ ) . 'js/script.js', array('jquery'), '0.1.0', true);
$data = array(
'upload_url' => admin_url('async-upload.php'),
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('media-form')
);
wp_localize_script( 'image-form-js', 'mup_config', $data );
}
add_action('wp_enqueue_scripts', 'mup_load_scripts');
Регистрируем шорткод для вывода формы
Для вывода формы загрузки файла мы будем использовать шорткод, таким образом мы сможем вставить форму где захотим и нам не придется каждый раз вставлять одну и ту же HTML разметку.
Нашей форме потребуется:
- Текстовое поле для ввода имени.
- Текстовое поля для ввода почты.
- Поле для файла с именем async-upload.
- Несколько div’ов для показа сообщений.
Также, если пользователь не авторизован, вместо формы для загрузки мы будем показывать форму для входа.
function mup_image_form_html(){
ob_start();
?>
<?php if ( is_user_logged_in() ): ?>
<p class="form-notice"></p>
<form action="" method="post" class="image-form">
<?php wp_nonce_field('image-submission'); ?>
<p><input type="text" name="user_name" placeholder="Your Name" required></p>
<p><input type="email" name="user_email" placeholder="Your Email Address" required></p>
<p class="image-notice"></p>
<p><input type="file" name="async-upload" class="image-file" accept="image/*" required></p>
<input type="hidden" name="image_id">
<input type="hidden" name="action" value="image_submission">
<div class="image-preview"></div>
<hr>
<p><input type="submit" value="Submit"></p>
</form>
<?php else: ?>
<p>Please <a href="<?php echo esc_url( wp_login_url( get_permalink() ) ); ?>">login</a> first to submit your image.</p>
<?php endif; ?>
<?php
$output = ob_get_clean();
return $output;
}
add_shortcode('image_form', 'mup_image_form_html');
Этот код регистрирует новый шорткод image_form. Мы используем функции управления буфером вывода, и можем писать HTML код внутри PHP функции. С помощью атрибута accept мы разрешаем загружать только изображения. В функцию wp_login_url() мы передаем ссылку на текущую страницу (get_permalink()), таким образом пользователь будет перенаправлен обратно в случае успешного входа.
Разрешаем определенным ролям загружать файлы
Чтобы наш плагин работал, нам надо разрешить пользователям с ролью subscriber загружать файлы, так как по-умолчанию роль subscriber не имеет прав на загрузку файлов.
function mup_allow_subscriber_to_uploads() {
$subscriber = get_role('subscriber');
if ( ! $subscriber->has_cap('upload_files') ) {
$subscriber->add_cap('upload_files');
}
}
add_action('admin_init', 'mup_allow_subscriber_to_uploads');
Теперь, когда основа плагина готова, мы можем создать новую страницу и вывести на ней нашу форму.

Так форма будет выглядеть на сайте с темой twentysixteen:

Если мы не авторизованы, то мы увидим ссылку на страницу входа:

Загрузка файлов с помощью AJAX
Теперь можно заняться реализацией основной задачи плагина.
В файле script.js объявим нужные переменные:
(function($) {
$(document).ready(function() {
var $formNotice = $('.form-notice');
var $imgForm = $('.image-form');
var $imgNotice = $imgForm.find('.image-notice');
var $imgPreview = $imgForm.find('.image-preview');
var $imgFile = $imgForm.find('.image-file');
var $imgId = $imgForm.find('[name="image_id"]');
});
})(jQuery);
Эти переменные пригодятся нам в дальнейшем.
Мы будем отслеживать событие change у поля для выбора файла и если пользователь выберет файл, мы начнем AJAX загрузку.
$imgFile.on('change', function(e) {
e.preventDefault();
var formData = new FormData();
formData.append('action', 'upload-attachment');
formData.append('async-upload', $imgFile[0].files[0]);
formData.append('name', $imgFile[0].files[0].name);
formData.append('_wpnonce', su_config.nonce);
$.ajax({
url: su_config.upload_url,
data: formData,
processData: false,
contentType: false,
dataType: 'json',
type: 'POST',
success: function(resp) {
console.log(resp);
}
});
});
Теперь отвлечемся немного от кода и попробуем загрузить файл. Используя инструменты разработчика (зависит от вашего браузера), проверим вывод в консоли. Успешный ответ от файла async-upload.php должен выглядеть примерно так:

Вы так же можете проверить существует ли файл в папке wp-content/uploads. Теперь, когда загрузка работает исправно, можно заняться улучшением нашего плагина:
- Покажем прогресс загрузки или уведомление во время загрузки файла.
- Покажем загруженное изображение в случае успеха или сообщение в случае ошибки.
- Добавим возможность загрузить новое изображение взамен старого.
Показываем сообщение в процессе загрузки
Чтобы показать сообщение о том, что файл загружается нам достаточно определить событие beforeSend в AJAX запросе:
beforeSend: function() {
$imgFile.hide();
$imgNotice.html('Uploading…').show();
},
Мы используем пустой div с классом image-notice для показа сообщения и прячем файловый инпут.
Мы также можем показать процент загрузки файла. Для этого надо переопределить стандартный jQuery объект xhr. Добавьте этот код в объявление $.ajax:
xhr: function() {
var myXhr = $.ajaxSettings.xhr();
if ( myXhr.upload ) {
myXhr.upload.addEventListener( 'progress', function(e) {
if ( e.lengthComputable ) {
var perc = ( e.loaded / e.total ) * 100;
perc = perc.toFixed(2);
$imgNotice.html('Uploading…(' + perc + '%)');
}
}, false );
}
return myXhr;
}
В браузерах, которые поддерживают эту функцию будет показан прогресс загрузки файла, а в тех, которые не поддерживают — ничего не произойдет.
Вывод изображения после успешной загрузки
В зависимости от ответа, который пришлет файл async-upload.php, мы показываем пользователю разные сообщения. Но если ключ success равен true, то мы можем показать загруженное изображение и спрятать поле выбора файла. Если произошла ошибка мы покажем сообщение об ошибке.
success: function(resp) {
if ( resp.success ) {
$imgNotice.html('Successfully uploaded.');
var img = $('<img>', {
src: resp.data.url
});
$imgId.val( resp.data.id );
$imgPreview.html( img ).show();
} else {
$imgNotice.html('Fail to upload image. Please try again.');
$imgFile.show();
$imgId.val('');
}
}
$imgId — скрытый инпут, в который записывается ID загруженного изображения, он пригодится нам дальше.
Заменяем старое изображение новым
Предоставим пользователю возможность заменить ранее загруженное изображение новым.
Для этого в случае успеха покажем немного другое сообщение:
$imgNotice.html('Successfully uploaded. <a href="#" class="btn-change-image">Change?</a>');
Теперь у нас есть ссылка с классом btn-change-image, на которую мы повесим обработчик события click. При нажатии на нее мы удалим текущее изображение, сообщение о загрузке и снова покажем поле для выбора файла.
$imgForm.on( 'click', '.btn-change-image', function(e) {
e.preventDefault();
$imgNotice.empty().hide();
$imgFile.val('').show();
$imgId.val('');
$imgPreview.empty().hide();
});
Нам также необходимо перезаписать значение файлового инпута, чтобы событие change сработало снова.
$imgFile.on('click', function() {
$(this).val('');
$imgId.val('');
});
Завершаем плагин
Для полноценной работы формы на нужно отслеживать событие submit и отправлять AJAX запрос:
$imgForm.on('submit', function(e) {
e.preventDefault();
var data = $(this).serialize();
$.post( su_config.ajax_url, data, function(resp) {
if ( resp.success ) {
$formNotice.css('color', 'green');
$imgForm[0].reset();
$imgNotice.empty().hide();
$imgPreview.empty().hide();
$imgId.val('');
$imgFile.val('').show();
} else {
$formNotice.css('color', 'red');
}
$formNotice.html( resp.data.msg );
});
});
Этот код использует встроенный в WordPress обработчик AJAX запросов. В случае успешного запроса мы удалим показанную картинку и поменяем цвет уведомления на зеленый. В случае ошибки поменяем цвет на красный.
Теперь откроем главный файл плагина и добавим обработчик для нашего AJAX запроса:
add_action('wp_ajax_image_submission', 'mup_image_submission_cb');
В нашем примере мы просто вышлем, уведомление администратору о новой загрузке.
function mup_image_submission_cb() {
check_ajax_referer('image-submission');
$user_name = filter_var( $_POST['user_name'],FILTER_SANITIZE_STRING );
$user_email = filter_var( $_POST['user_email'], FILTER_VALIDATE_EMAIL );
$image_id = filter_var( $_POST['image_id'], FILTER_VALIDATE_INT );
if ( ! ( $user_name && $user_email && $image_id ) ) {
wp_send_json_error( array('msg' => 'Validation failed. Please try again later.') );
}
$to = get_option('admin_email');
$subject = 'New image submission!';
$message = sprintf(
'New image submission from %s (%s). Link: %s',
$user_name,
$user_email,
wp_get_attachment_url( $image_id )
);
$result = wp_mail( $to, $subject, $message );
if ( $result ) {
wp_send_json_error( array('msg' => 'Email failed to send. Please try again later.') );
} else {
wp_send_json_success( array('msg' => 'Your submission successfully sent.') );
}
}
Для отправки сообщения об успешной или не очень загрузке используются функции wp_send_json_error() и wp_send_json_success().
Для проверки работы плагина попробуйте загрузить файл и проверьте пришло ли вам уведомление на почту.





Здравствуйте, спасибо за пример, то что надо.
Только не пойму как поменять путь куда будет загружаться фотография?
по умолчанию она загружается в папку uploads/год/месяц
а я хочу в корень и в папку с номером пользователя