ezcode

Просто о сложном.

Загрузка файлов с помощью AJAX для вашего WordPress плагина

16-10-07 Php, WordPress WordPress 1
(追記) (追記ここまで)

Загрузка файлов для плагина 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');

Теперь, когда основа плагина готова, мы можем создать новую страницу и вывести на ней нашу форму.

[画像:Загрузка файлов с помощью AJAX в WordPress]

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

[画像:Загрузка файлов с помощью AJAX в WordPress]

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

[画像:Загрузка файлов с помощью AJAX в WordPress]

Загрузка файлов с помощью 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 должен выглядеть примерно так:

[画像:Загрузка файлов с помощью AJAX в WordPress]

Вы так же можете проверить существует ли файл в папке wp-content/uploads. Теперь, когда загрузка работает исправно, можно заняться улучшением нашего плагина:

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

Показываем сообщение в процессе загрузки

Чтобы показать сообщение о том, что файл загружается нам достаточно определить событие beforeSend в AJAX запросе:

beforeSend: function() {
 $imgFile.hide();
 $imgNotice.html('Uploading&hellip;').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&hellip;(' + 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().

Для проверки работы плагина попробуйте загрузить файл и проверьте пришло ли вам уведомление на почту.

Хочешь получать статьи на почту?

Подпишись на обновления!
* Ваш email не будет разглашен/продан. Вы сможете отписаться в любое время.

1 Комментарий

  1. Smi says:

    Здравствуйте, спасибо за пример, то что надо.

    Только не пойму как поменять путь куда будет загружаться фотография?

    по умолчанию она загружается в папку uploads/год/месяц
    а я хочу в корень и в папку с номером пользователя

Добавить комментарий Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *


Notice: Undefined variable: panelfloat in /var/www/gzaqhtoc/data/www/easy-code.ru/wordpress/wp-content/plugins/socialize-it/inc/SocializeIt.php on line 235

Notice: Undefined index: onclick in /var/www/gzaqhtoc/data/www/easy-code.ru/wordpress/wp-content/plugins/socialize-it/inc/SocializeIt.php on line 253

Notice: Undefined index: onclick in /var/www/gzaqhtoc/data/www/easy-code.ru/wordpress/wp-content/plugins/socialize-it/inc/SocializeIt.php on line 253

Notice: Undefined index: onclick in /var/www/gzaqhtoc/data/www/easy-code.ru/wordpress/wp-content/plugins/socialize-it/inc/SocializeIt.php on line 253

Notice: Undefined index: onclick in /var/www/gzaqhtoc/data/www/easy-code.ru/wordpress/wp-content/plugins/socialize-it/inc/SocializeIt.php on line 253

AltStyle によって変換されたページ (->オリジナル) /