WordPress как на ладони

API настроек для сети сайтов (мультисайт)

В WordPress есть специальное API, которое позволяет без шума и пыли создавать страницы настроек (опций), например настройки плагина или темы. Данные таких опций записываться в таблицу опций. API позволяет создавать такие страницы быстро, с защитой и в логике и дизайне самого WordPress, т.е. с использованием API не нужно отдельно заботиться о HTML коде и CSS стилях формы, о необходимой защите и в целом логичности всего кода - все это API берет на себя.

Теме API настроек посвящена отдельная статья, где подробно объясняется как работает и как использовать это самое API.

Эта заметка является дополнением к вышеупомянутой статье и рассказывает, как создать страницу настроек в меню «сети сайтов» (админка мультисайта).

Дело в том, что при активации MU режима появляются отдельные сайты с привычной админкой и появляется раздел «Управление сетью» (админка сети), где настраивается вся сеть.

Админка сети сайтов: /wp-admin/network

Так вот, API опций по умолчанию не рассчитан на использование в настройках сети. Точнее использовать основную логику API там можно, но «кое что» нужно будет учитывать и доделывать. Об этом «кое что» поговорим в этой заметке.

Как использовать «API опций» WordPress на страницах админки сети сайтов (Мультисайт)

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

Суть проблемы

В стандартном варианте API регистрируется опция с помощью register_setting() и её значения отправляются POST запросом в файл /wp-admin/options.php. Далее WordPress все делает за нас: получает, защищает, обрабатывает и сохраняет данные опций в таблицу wp_options. Однако в сети мультисайт опции нужно сохранять в другую таблицу - wp_sitemeta, а на это логика API не рассчитана... По этому вопросу был создан тикет, но решения из коробки так и не появилось, впрочем оно не особо нужно, потому что задача легко решается, если подумать.

Напомню о различиях. В MU сборке WordPress опции отдельного сайта обрабатываются функциями get_option(), update_option() и т.д., а опции сети обрабатываются через get_network_option() (обертка: get_site_option()), update_network_option() (обертка: update_site_option()). Принципиальная разница в том, что данные сохраняются в разные таблицы, и настройки сайта у каждого свои, а настройки сети - это общие настройки для всех сайтов.

Чтобы не писать тут кучу букв от которых голова пойдет кругом, я покажу разницу в сравнении. Для этого покажу как создается стандартная страница настроек и как такая же страница создается для настроек сети. Меняется около 10% кода...

Обычная страница настроек, как предполагает API опций:

<?php

## Создаем страницу настроек плагина
add_action( 'admin_menu', 'add_plugin_page' );
function add_plugin_page(){
	add_options_page( 'Настройки плагина', 'Мой плагин', 'manage_options', 'myplug_slug', 'options_page_html' );
}

## HTML код страницы
function options_page_html(){
	?>
	<div class="wrap">
		<h2><?php echo get_admin_page_title() ?></h2>

		<form action="options.php" method="POST">
			<?php
			settings_fields( 'option_group' );     // скрытые защитные поля - nonce

			do_settings_sections( 'myplug_page' ); // секции с настройками (опциями). У нас она всего одна 'section_id'

			submit_button();
			?>
		</form>
	</div>
	<?php
}

## Регистрируем настройки. Настройки будут храниться в массиве, а не одна настройка = одна опция
add_action( 'admin_init', 'my_plugin_settings' );
function my_plugin_settings(){
	// создаем опцию
	register_setting( 'option_group', 'option_name', 'sanitize_callback' );

	// создаем секцию с полями
	add_settings_section( 'section_id', 'Основные настройки', '', 'myplug_page' );

	// создаем поля для секции
	$opt_name = 'my_option';
	add_settings_field( $opt_name, 'Моя опция', 'fill_field', 'myplug_page', 'section_id', $opt_name );

	$opt_name = 'my_option_two';
	add_settings_field( $opt_name, 'Моя вторая опция', 'fill_field', 'myplug_page', 'section_id', $opt_name );
}

## Заполняем опцию 1
function fill_field( $opt_name ){
	$opts      = get_option( 'option_name' );
	$name_attr = "option_name[$opt_name]";
	$val       = isset( $opts[ $opt_name ] ) ? $opts[ $opt_name ] : null;

	if( $opt_name === 'my_option' ){
		echo '<input type="text" name="'. $name_attr .'" value="'. esc_attr( $val ) .'" />';
	}

	if( $opt_name === 'my_option_two' ){
		echo '<label><input type="checkbox" name="'. $name_attr .'" value="1" '. checked( 1, $val, 0 ) .' /> галка или нет?</label>';
	}
}

## Очистка сохраняемых данных
function sanitize_callback( $options ){
	foreach( $options as $name => & $val ){
		if( $name == 'my_option' )     $val = sanitize_text_field( $val );

		if( $name == 'my_option_two' ) $val = intval( $val );
	}

	return $options;
}

В результате получим такую страницу:

Такая же страница настроек, только для сети сайтов:

<?php

## NETWORK - Создаем страницу настроек плагина
add_action( 'network_admin_menu', 'add_plugin_page' );
function add_plugin_page(){
	add_submenu_page( 'settings.php', 'Настройки плагина', 'Мой плагин', 'manage_options', 'myplug_slug', 'options_page_html' );
}

## HTML код страницы
function options_page_html(){
	?>
	<div class="wrap">
		<h2><?php echo get_admin_page_title() ?></h2>

		<form action="edit.php?action=myplug_options" method="POST">
			<?php
			wp_nonce_field( 'myplug_nonce' ); // NETWORK - settings_fields() не подходит для мультисайта...

			do_settings_sections( 'myplug_page' ); // секции с настройками (опциями). У нас она всего одна 'section_id'

			submit_button();
			?>
		</form>
	</div>
	<?php
}

## Регистрируем настройки. Настройки будут храниться в массиве, а не одна настройка = одна опция
add_action( 'admin_init', 'my_plugin_settings' );
function my_plugin_settings(){
	// NETWORK - ловим обновления опций через хук 'network_admin_edit_(action)'
	if( is_multisite() )
		add_action( 'network_admin_edit_'.'myplug_options', 'myplug_options_update' );

	// создаем опцию
	register_setting( 'option_group', 'option_name', 'sanitize_callback' );

	// создаем секцию с полями
	add_settings_section( 'section_id', 'Основные настройки', '', 'myplug_page' );

	// создаем поля для секции
	$opt_name = 'my_option';
	add_settings_field( $opt_name, 'Моя опция', 'fill_field', 'myplug_page', 'section_id', $opt_name );

	$opt_name = 'my_option_two';
	add_settings_field( $opt_name, 'Моя вторая опция', 'fill_field', 'myplug_page', 'section_id', $opt_name );
}

## Заполняем опцию 1
function fill_field( $opt_name ){
	$opts      = get_site_option( 'option_name' ); // NETWORK - не get_option()

	$name_attr = "option_name[$opt_name]";
	$val       = isset( $opts[ $opt_name ] ) ? $opts[ $opt_name ] : null;

	if( $opt_name === 'my_option' ){
		echo '<input type="text" name="'. $name_attr .'" value="'. esc_attr( $val ) .'" />';
	}

	if( $opt_name === 'my_option_two' ){
		echo '<label><input type="checkbox" name="'. $name_attr .'" value="1" '. checked( 1, $val, 0 ) .' /> галка или нет?</label>';
	}
}

## Очистка сохраняемых данных
function sanitize_callback( $options ){
	foreach( $options as $name => & $val ){
		if( $name == 'my_option' )     $val = sanitize_text_field( $val );

		if( $name == 'my_option_two' ) $val = intval( $val );
	}

	return $options;
}

## NETWORK - обновляем опции в БД
function myplug_options_update(){
	// nonce check
	check_admin_referer( 'myplug_nonce' );

	update_site_option( 'option_name', wp_unslash( $_POST['option_name'] ) );

	wp_redirect( network_admin_url( 'settings.php?page=myplug_slug&updated=true' ) );
	exit;
}

В результате получим такую страницу:

Отличия

Распишу все отличия, чтобы не мучиться и не искать их, некоторые из которых вы в результате можете и не заметить.

  1. Хук network_admin_menu вместо admin_menu для реги страницы опций.

  2. add_submenu_page( 'settings.php' вместо add_options_page( - создаем подстраницу в другом меню.

  3. <form action="edit.php?action=myplug_options" вместо <form action="options.php" - отправляем POST запрос в файл /network/edit.php - это специальный файл для таких задач.

  4. wp_nonce_field() вместо settings_fields() - для защиты. Родная функция API тут не подходит...

  5. get_site_option() вместо get_option() - сохраняем опции в другую таблицу.

  6. Добавился хук network_admin_edit_(action) и функция для этого хука - myplug_options_update(), которая сохраняет наши опции с помощью update_site_option(). Для сети автоматический перехват запроса и его обработка, которую предусматривает API, не работает.

А вот так эти отличия выглядят:

<?php

## NETWORK - Создаем страницу настроек плагина
add_action( 'network_admin_menu', 'add_plugin_page' );
function add_plugin_page(){
	add_submenu_page( 'settings.php', 'Настройки плагина', 'Мой плагин', 'manage_options', 'myplug_slug', 'options_page_html' );
}

## HTML код страницы
function options_page_html(){
	?>
	<form action="edit.php?action=myplug_options" method="POST">
		<?php
		wp_nonce_field( 'myplug_nonce' ); // NETWORK - settings_fields() не подходит для мультисайта...

		// ...
		?>
	</form>
	<?php
}

## Регистрируем настройки. Настройки будут храниться в массиве, а не одна настройка = одна опция
add_action( 'admin_init', 'my_plugin_settings' );
function my_plugin_settings(){
	// NETWORK - ловим обновления опций через хук 'network_admin_edit_(action)'
	if( is_multisite() )
		add_action( 'network_admin_edit_'.'myplug_options', 'myplug_options_update' );

	// ...
}

## Заполняем опцию 1
function fill_field( $opt_name ){
	$opts      = get_site_option( 'option_name' ); // NETWORK - не get_option()

	// ...
}

## NETWORK - обновляем опции в БД
function myplug_options_update(){
	// nonce check
	check_admin_referer( 'myplug_nonce' );

	update_site_option( 'option_name', $wp_unslash( $_POST['option_name'] ) );

	wp_redirect( network_admin_url( 'settings.php?page='. 'myplug_slug' .'&updated=true' ) );
	exit;
}

-

Успехов в создании MU сайтов!

1 комментарий
    Войти