Статьи

Включение загрузки файлов AJAX в ваш плагин WordPress

Предоставление функции загрузки в нашем плагине — дело непростое. Мы должны быть в состоянии обеспечить хороший пользовательский интерфейс (UX) для загрузки, а также следить за проблемами безопасности, которые сопровождают его. Если это не сделано должным образом, мы потенциально можем подвергнуть сайт риску возникновения любых уязвимостей безопасности.

Вместо того, чтобы создавать все решение с нуля, мы можем использовать основной код WordPress в наших интересах, чтобы ускорить разработку, особенно используя файл async-upload.php который находится в каталоге wp-admin .

Использование файла async-upload.php имеет несколько преимуществ. Во-первых, поскольку он используется ядром WordPress для асинхронной загрузки в медиатеку, мы можем быть уверены, что код соответствует стандарту. Кроме того, все проверки и проверки привилегий были выполнены, поэтому нам не нужно делать это самим.

Требования

Есть несколько правил, которые необходимо соблюдать, если мы хотим использовать этот скрипт. Вот разбивка на каждого из них.

  • Используемый входной file должен иметь атрибут name, установленный на async-upload

Это связано с тем, что после проверки в файле async-upload.php , wp_ajax_upload_attachment который затем вызывает функцию media_handle_upload которая использует async-upload в качестве первых аргументов. Использование любого другого значения не будет работать.

  • Одноразовый номер, который мы отправили вместе с запросом AJAX, должен использовать ключ _wpnonce по умолчанию со значением, сгенерированным из функции wp_create_nonce('media-form')

Это связано с проверкой в ​​форме check_ajax_referer которая происходит внутри функции wp_ajax_upload_attachment .

  • Данные, отправляемые с помощью запроса AJAX, также должны иметь ключ с именем action со значением upload-attachment

Это проверяется внутри файла async-upload.php который будет запускать функцию wp_ajax_upload_attachment только wp_ajax_upload_attachment когда значение установлено правильно.

О плагине

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

Для целей этого урока мы собираемся создать плагин, который позволит зарегистрированным пользователям отправлять изображения для какого-то рода конкурсов. У нас будет форма отправки через веб-интерфейс. Это означает, что форма загрузки будет отображаться на определенной странице, где пользователь может напрямую загрузить изображение. Это идеальный кандидат для реализации функции загрузки AJAX.

Поскольку мы пытаемся сделать вещи простыми, давайте определим некоторые рекомендации относительно того, что этот плагин будет и не будет делать, ради длины этого урока.

Плагин сможет:

  • Разрешить администратору прикрепить форму к любой странице с помощью шорткода.
  • Показать зарегистрированным пользователям форму отправки с функцией загрузки AJAX.
  • Отправить электронное письмо, чтобы уведомить администратора сайта при представлении.

В рамках данного руководства плагин не будет:

  • Храните любые материалы в базе данных.
  • Просмотр представлений в бэкэнде.
  • Разрешить анонимным пользователям загружать файлы.

Начальная загрузка плагина

wp-content/plugins папку wp-content/plugins и создайте новую папку, в которой будут находиться все наши коды плагинов. Я буду использовать имя sitepoint-upload для остальной части этого урока с префиксом su_ для всех функций и перехватывает обратные вызовы.

Затем создайте основной файл плагина с тем же именем, что и папка, чтобы упростить задачу. Внутри папки плагина у нас также будет папка js которая пока содержит пустой файл script.js .

Вот обновленная структура каталогов для нашего плагина.

 wp-content/ |-- plugins/ |-- sitepoint-upload/ |-- js/ | |-- script.js |--sitepoint-upload.php 

Давайте sitepoint-upload.php простой заголовок плагина в основной файл плагина sitepoint-upload.php , а затем перейдем на страницу плагинов, чтобы активировать его. Вот мой пример:

 <?php  

Поставьте в очередь сценарий

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

 function su_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', 'su_load_scripts'); 

Мы также собираемся локализовать некоторые данные, которые будут использоваться внутри script.js используя функцию wp_localize_script . Нам нужны три вещи: правильный URL-адрес для admin-ajax.php поскольку мы собираемся отправить форму также через AJAX, а также URL-адрес для файла async-upload.php . Третий элемент, который нам нужно локализовать — это nonce, который будет сгенерирован с wp_create_nonce функции wp_create_nonce .

Обновленная функция обратного вызова для нашего хука wp_enqueue_scripts выглядит следующим образом:

 function su_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', 'su_config', $data ); } add_action('wp_enqueue_scripts', 'su_load_scripts'); 

Зарегистрируйте шорткод для формы отправки

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

  • Текстовое поле для ввода имени пользователя
  • Другое поле ввода адреса электронной почты для адреса электронной почты пользователя
  • Ввод файла async-upload для загрузки AJAX
  • Группа элементов-заполнителей, которые будут использоваться для предварительного просмотра электронной почты, сообщений об ошибках и других элементов.

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

 function su_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', 'su_image_form_html'); 

Несколько объяснений о функции обратного вызова шорткода выше:

  • Шорткод, который мы регистрируем, это image_form .
  • Мы используем буферизацию вывода, чтобы мы могли быть более гибкими с тем, что мы выставляем внутри функции обратного вызова шорткода.
  • Мы также ограничиваем выбор файлов для изображений только с помощью атрибута accept на входе файла. Обратите внимание, что это не заменяет фактическую проверку файла. (Больше информации)
  • В качестве URL для входа мы предоставляем текущую постоянную ссылку на страницу в wp_login_url чтобы при успешном входе пользователь был перенаправлен обратно на нашу страницу wp_login_url .

Добавить возможность upload_files в роли определенного пользователя

Чтобы убедиться, что наш плагин работает правильно, нам нужно изменить возможность роли subscriber потому что по умолчанию пользователи с subscriber роли не имеют возможности загружать файлы.

 function su_allow_subscriber_to_uploads() { $subscriber = get_role('subscriber'); if ( ! $subscriber->has_cap('upload_files') ) { $subscriber->add_cap('upload_files'); } } add_action('admin_init', 'su_allow_subscriber_to_uploads'); 

Обратите внимание, что роль subscriber будет изменена только в том случае, если у нее все еще нет возможности upload_files .

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

Отправить ваше изображение WordPress

Вот так выглядит форма в twentysixteen интерфейсе при установке WordPress по умолчанию с twentysixteen темой twentysixteen .

Форма отправки внешнего интерфейса (залогинен)

Если мы вышли из сайта, вместо этого будет показано уведомление.

Форма отправки внешнего интерфейса (Выйти)

Похоже, наш плагин прекрасно сработался!

Реализация загрузки AJAX

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

Давайте откроем наш файл script.js который находится внутри папки js чтобы продолжить. Сначала мы закроем весь код в выражении функции, вызываемой немедленно (IIFE) .

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

 (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); 

Кэшированные селекторы будут полезны нам в долгосрочной перспективе. Как упоминалось ранее, существует несколько правил, которые необходимо соблюдать, чтобы пройти проверку в файле async-upload.php . Для этого мы сделаем POST-запрос через AJAX в файл async-upload.php с правильными парами ключей или значений, как указано. Это можно сделать с помощью API FormData .

Сначала мы подключим событие 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 при успешной загрузке, выглядит следующим образом:

загрузить php

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

  • Показать индикатор выполнения или текст во время процесса загрузки.
  • Покажите предварительный просмотр загруженного изображения при успешной загрузке.
  • Ошибка отображения, если загрузка не удалась.
  • Предоставьте пользователю возможность загрузить новое изображение, чтобы заменить текущее.

Давайте посмотрим, как сделать это один за другим.

Показать индикатор выполнения или текст во время процесса загрузки

Это на самом деле простой. Нам нужно только определить обратный вызов для beforeSend jQuery AJAX. Где-нибудь в коде для загрузки 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 , мы можем показать загруженное изображение пользователю и скрыть введенный файл. Если загрузка не удалась, мы заменим текст внутри div на image-notice ранее.

 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 — это скрытый ввод, который мы используем для ссылки на идентификатор загруженного изображения. Мы собираемся использовать это значение позже для отправки формы, так что пока не беспокойтесь об этом.

Предоставьте пользователю способ загрузить новое изображение, чтобы заменить текущее

Что мы собираемся сделать, это предоставить ссылку в качестве метода для пользователя, чтобы заменить загруженное в настоящее время изображение новым. Мы изменим уведомление, отображаемое после успешного завершения загрузки с:

 $imgNotice.html('Successfully uploaded.'); 

в

 $imgNotice.html('Successfully uploaded. <a href="#" class="btn-change-image">Change?</a>'); 

Теперь, когда у нас есть якорь с классом btn-change-image , мы будем использовать его в наших интересах. Затем мы можем добавить прослушиватель события щелчка на этом якоре, когда он щелкает, он удаляет текущий предварительный просмотр изображения. Мы также будем скрывать уведомление, а также снова отображать ввод файла с его значением, которое было сброшено.

 $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(''); }); 

Прежде чем мы перейдем к следующему разделу, давайте еще раз пройдемся по функции загрузки и посмотрим, все ли работает как задумано.

Завершение плагина

Мы собираемся обработать отправку формы через AJAX, поэтому мы привязываем слушателя события к событию submit этой формы.

 $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. Поскольку мы устанавливаем значение action в image_submission , нам нужно добавить действительный обратный вызов в действие wp_ajax_image_submission .

 add_action('wp_ajax_image_submission', 'su_image_submission_cb'); 

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

Вот полный код для функции обратного вызова AJAX:

 function su_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.') ); } } 

Для нашей цели достаточно простой проверки check_ajax_referer и встроенной PHP-функции filter_var для нашего filter_var использования. Мы также собираемся использовать функции wp_send_json_error и wp_send_json_success для отправки ответа.

На этом наш плагин закончен и полностью функционален. Для проверки попробуйте правильно заполнить форму и посмотреть, получено ли письмо с ссылкой на загруженное изображение.

Дальнейшие улучшения

Поскольку цель этого руководства — продемонстрировать, как выполнять загрузку AJAX через внутренний файл async-upload.php , мы определенно сокращаем ситуацию в нескольких местах. Вот несколько советов, которые могут улучшить наш простой плагин в целом.

  • Добавление дополнительных полей в форму для сбора любых дополнительных значений для отправки.
  • Поставьте отдельный CSS-файл в очередь, чтобы лучше оформить форму, заметить и загрузить информацию.
  • Сохраните отправленные данные в базу данных, чтобы мы могли просмотреть их снова.
  • Выполнение большей проверки процесса загрузки, чтобы сделать его более безопасным.

Полный исходный код плагина доступен на GitHub .

Вывод

В заключение, реализация загрузки AJAX в плагине может быть ускорена, если мы знаем, где искать. Используя файл async-upload.php , мы можем сократить время разработки для реализации этой функции, а также получить некоторую уверенность, поскольку этот же файл используется ядром WordPress для обработки загрузки пользователя на панели администрирования.