AJAX в админке. Безопасность

Урок 3

Зарегистрируйтесь или войдите и получите бесплатный доступ к первому уроку курса.

Урок 3

AJAX в админке WordPress (с обработчиком admin-ajax.php)

Если вы пишете асинхронные запросы для админки WordPress, а не для самого сайта, то вам следует знать несколько принципиальных отличий. В этом уроке я расскажу про эти отличия, также поговорим за безопасность.

Чтобы скачать готовый код из этого урока, вам нужно купить курс.

1. Способы подключения JS

Если вы не хоатите использовать отдельный JS-файл для ваших запросов, то вы можете вставить JavaScript-код непосредственно в HTML админки:

  • admin_head – внутри тегов </head>,
  • admin_footer – перед закрывающим тегом </body>.

Например:

add_action( 'admin_head', 'true_javascript_in_admin', 25 );
 
function true_javascript_in_admin() {
 
	echo "<script>alert( 'привет' )</script>";
 
}

Способ 2. При помощи хука admin_enqueue_scripts

В том случае, если вы готовы создать отдельный JS-файл для вашего запроса, например назовём его true-admin-ajax.js, то его можете подключить хуком admin_enqueue_scripts.

add_action( 'admin_enqueue_scripts', 'true_js_in_admin', 25 );
 
function true_js_in_admin() {
 
	wp_enqueue_script( 'true-adminajax', get_stylesheet_directory_uri() . '/true-admin-ajax.js', array( 'jquery' ) );
 
}

2. URL запроса

В предыдущих уроках мы сами динамически выводили URL AJAX-запроса и передавали его в скрипт функцией wp_localize_script(). В админке же ничего этого делать не нужно, URL обработчика AJAX всегда доступен в переменной ajaxurl.

jQuery.ajax({
	type : 'POST',
	url : ajaxurl,
	data : 'action=myaction&input_val=' + my_value,
	success : function(data){
		jQuery('#response').html(data);
	}
});

3. PHP-обработчик запроса

Опять-таки, в предыдущих уроках для написания обработчика AJAX-запроса мы использовали несколько хуков, а именно:

  • wp_ajax_ – для авторизованных пользователей.
  • wp_ajax_nopriv_ – для неавторизованных пользователей.

Как вы думаете, понадобится ли нам в админке второй хук? Конечно же нет, ведь в админке бывают только авторизованные пользователи!

// wp_ajax_ - только для зарегистрированных пользователей
add_action( 'wp_ajax_myaction', 'true_function' ); // wp_ajax_{значение параметра action}
 
function true_function(){
	update_option( 'my_settings', sanitize_text_field( $_POST[ 'input_val' ] ) );
	echo 'Привет, значение ' . esc_html( $_POST[ 'input_val' ] ) . ' сохранено.';
	die();
}

4. Безопасность запросов с использованием одноразовых чисел

На самом деле это нужно не только в админке, но самое время о них сейчас поговорить.

В общем для того, чтобы ваши AJAX-запросы не выполнил кто-то, кто их не должен выполнять, то вам следует их защитить. Для этого в WordPress существует механизм WP Nonces (одноразовых чисел).

Одноразовые числа уникальны в зависимости от сессии пользователя, поэтому запрос не сможет выполнить кто-то левый.

4.1 Создаём одноразовое число (токен)

При создании одноразовых чисел можете воспользоваться одной из следующих функций:

  • wp_nonce_field() – эта функция сразу же выведет поле типа «hidden» и запишет в него одоразовое число, например wp_nonce_field( 'hello' ) выведет <input type="hidden" name="_wpnonce" value="значение" />.
  • Есть ещё функция wp_create_nonce() – она лишь создаёт одноразовое число и возвращает его в виде строки.

Например, если вы передаёте данные в запросе через $('#form').serialize(), то вам больше подойдёт функция wp_nonce_field().

4.2 Проверяем одноразовое число в обработчике AJAX

Для проверки правильности одноразового числа в самое начало кода нашего запроса мы вставляем функцию check_ajax_referer(), в качестве первого параметра передаём секретный ключ одноразового числа.

Подробнее про это всё в видео.

Пример. Изменение значений произвольных полей постов «на лету».

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

На самом деле есть два варианта решения, но мы рассмотрим только тот, который по теме.

Шаг 1. Добавление колонки с ценой в список товаров

Более подробно о колонках в списках постов я писал здесь, кстати, возможно плагин магазина, который вы используете (если используете) уже мог сам надобавлять колонок, тогда вам потребуется лишь немного отредактировать их содержимое.

Вот готовый код для колонок.

Обязательно убедитесь, что тип поста на 16-й строчке кода соответствует вашему (слово product)!

function true_add_post_columns($my_columns){
	$slider = array( 'price' => 'Цена' );
	$my_columns = array_slice( $my_columns, 0, 5, true ) + $slider + array_slice( $my_columns, 5, NULL, true );
	return $my_columns;
}
 
function true_fill_post_columns( $column ) {
	global $post;
	switch ( $column ) {
		case 'price': {
			echo '<input type="text" class="this_price" data-id="' . $post->ID .'" data-nonce="' . wp_create_nonce( 'security12345' ) . '" value="' . intval( get_post_meta( $post->ID, '_price', true ) ) . '" /><p></p>';
			break;
		}
	}
}
 
add_filter( 'manage_edit-product_columns', 'true_add_post_columns', 10, 1 ); // manage_edit-{тип поста}_columns
add_action( 'manage_posts_custom_column', 'true_fill_post_columns', 10, 1 );

Не знаете, куда вставлять код?

У вас должно получиться что-то в этом роде:

добавление колонки с ценой в список товаров

По скриншоту виду, что никаких кнопок типа «Сохранить» к полю добавлено не было — на самом деле зачем они нам, если цену можно сохранять автоматически (при снятии фокуса), достаточно лишь кликнуть мышью за пределами поля ввода.

Шаг 2. Создание и подключение скриптов

Самый легкий шаг на самом деле, но при этом он очень важен. Итак, сначала нужно создать какой-нибудь пустой js-файл в папке с вашей текущей темой (важно, чтобы он находился в той же папке, что и functions.php, style.css). Назовите его как-нибудь, у меня это будет ajax-post-meta.js.

Теперь в functions.php добавим:

add_action( 'admin_enqueue_scripts', 'true_admin_scripts', 25 );
 
function true_admin_scripts() {
	wp_enqueue_script( 'ajaxpostmeta', get_stylesheet_directory_uri() . '/ajax-post-meta.js', array( 'jquery' ) );
}

Шаг 3. Асинхронный AJAX запрос. Содержимое файла ajax-post-meta.js

Если до этого шага вы делали всё в точности так, как я описывал, можете вставлять следующий код в ajax-post-meta.js, не задумываясь:

jQuery(function($){
	$('.this_price').blur(function(){
		thisPrice = $(this);
		$.ajax({
			type : 'POST',
			url : ajaxurl,
			data : {
				action : 'updateprice',
				price_val : thisPrice.val(),
				product_id : thisPrice.data('id'),
				_wpnonce : thisPrice.data('nonce')
			},
			beforeSend:function(xhr){
				thisPrice.attr('readonly','readonly').next().html('Сохраняю...');
			},
			success:function(results){
				thisPrice.removeAttr('readonly').next().html('<span style="color:#0FB10F">Сохранено</span>');
			}
		});
	});
});

Шаг 4. Функция-обработчик

В functions.php:

function updatePrice_callback(){ // название не имеет значения, но должно соответствовать названиям в хуках
 
	check_ajax_referer( 'security12345' );
 
	update_post_meta( $_POST[ 'product_id' ], '_price', intval( $_POST[ 'price_val' ] ) );
	die();
}
 
add_action( 'wp_ajax_updateprice', 'updatePrice_callback' );
// wp_ajax_nopriv_ не нужен, так как мы работаем в админке (а в админку не попадают незареганные пользователи)

В итоге у вас всё должно получиться, то есть сначала вводим цену в поле, потом щелкаем где-нибудь на странице (например переходим в другое поле), и цена сохраняется.

асинхронное сохранение цены на ajax
Купить курс
  • 7 видеоуроков
  • Доступ к теме курса с готовым кодом после каждого урока
  • Доступ навсегда
4000 р2800 р
Скидка 30% до 1 октября