Как сохранить статус открытых/закрытых и скрытых/показанных метабоксов для каждой публикации?
Моя настоящая проблема немного сложна, поэтому я постараюсь здесь абстрагироваться от нее и сделать ее простой.
Я работаю над пользовательским приложением на основе WordPress. Я зарегистрировал пользовательский тип записи, назовем его "люди", где я храню информацию о... людях.
CPT поддерживает только поля по умолчанию "Заголовок публикации" и "содержимое публикации", но есть несколько метабоксов для хранения информации о пользователях (например, мое приложение в качестве адресной книги).
Итак, есть метабокс для хранения личных информация, одна для хранения информации о социальных сетях, другая для хранения информации, связанной с работой, т. Е. Если этот человек для меня клиент, поставщик, если у нас есть кредиты или дебеты...
Я здесь упростил, но существует постоянное количество метабоксов, скажем, 12.
Моя проблема в том, что некоторые люди, для которых я хочу хранить информацию, являются просто случайными контактами, и я хочу хранить только личную информацию, другие - друзья, и я хочу хранить личную информацию и информацию о социальных сетях, другие - клиенты или поставщики и я хотим хранить информацию, связанную с работой.
Если при редактировании поста я скрываю (через меню опций экрана ) или закрываю любой ненужный мне метабокс, когда я открываю другой пост, где они мне нужны, я должен показать или открыть их снова. Это , потому что позиция/статус/порядок метабоксов сохраняются для каждого пользователя в качестве метаданных пользователя.
Если вы представляете, что в некоторых сообщениях мне нужно 2 метабокса, в некоторых 10 и в некоторых 5, вы понимаете, что это раздражает, потому что все они показаны/открыты сделайте экран редактирования малодоступным (полоса прокрутки кажется бесконечной), и иногда информация, которую я ищу, находится в конце страницы после нескольких метабоксов без информации...
Вопрос:
Можно ли сохранить позицию/статус/заказ метабоксов на основе каждой должности для определенного типа должности?
PS: Я знаю, что некоторые js/jQuery могут решить эту проблему, но, если возможно, я бы избегал решений javascript.
2 answers
Основная проблема:
Основная проблема здесь заключается в том, что в закрытии-, скрытие- и упорядочивание- вызовы ajax, идентификатор сообщения, отправленный с полезной нагрузкой, отсутствует. Вот два примера данных формы:
1) action:closed-postboxes
closed:formatdiv,tagsdiv-post_tag,trackbacksdiv,authordiv
hidden:slugdiv
closedpostboxesnonce:8723ee108f
page:post
2) action:meta-box-order
_ajax_nonce:b6b48d2d16
page_columns:2
page:post
order[side]:submitdiv,formatdiv,categorydiv,tagsdiv-post_tag,postimagediv
order[normal]:postexcerpt,postcustom,trackbacksdiv,commentsdiv,authordiv
order[advanced]:
Мы могли бы обойти это, используя другой пользовательский вызов ajax.
Мы, конечно, могли бы просто подключиться к save_post
и изменять данные при каждом сохранении записи. Но это не обычный пользовательский интерфейс, так что это не считается здесь
Есть еще одно неэлегантное решение, доступное с PHP, описанное здесь ниже:
Решение Без Javascript:
Вопрос в том, где хранить данные? Как пользовательметаданные, опубликовать метаданные или, может быть, в пользовательской таблице?
Здесь мы храним его как метаданные пользователя и берем в качестве примера закрытие почтовых мета-полей.
Когда мета-значение closedpostboxes_post
обновляется, мы сохраняем его в мета-значение closedpostboxes_post_{post_id}
как хорошо.
Затем мы перехватываем выборку closedpostboxes_post
, чтобы переопределить ее соответствующим мета-значением на основе идентификатора пользователя и идентификатора записи.
A) Обновление во время действия closed-postboxes
ajax:
Мы можем получить идентификатор записи через wp_get_referer()
, а затем использовать
удобную функцию url_to_postid()
. Я впервые узнал об этой "забавной" функции после прочтения ответа от @s_ha_dum несколько месяцев назад;-) К сожалению, функция не распознает переменные ?post=123
GET, но мы можем сделать маленький трюк, просто изменив его на p=123
, чтобы обойти его.
Мы можем подключиться к updated_user_meta
, который запускается сразу после обновления метаданных пользователя для closedpostboxes_post
:
add_action( 'updated_user_meta',
function ( $meta_id, $object_id, $meta_key, $_meta_value )
{
$post_id = url_to_postid( str_replace( 'post=', 'p=', wp_get_referer() ) );
if( 'closedpostboxes_post' === $meta_key && $post_id > 0 )
update_user_meta(
$object_id,
'closedpostboxes_post_' . $post_id,
$_meta_value
);
}
, 10, 4 );
Б) Извлечение данных:
Мы можем подключиться к get_user_option_closedpostboxes_post
, чтобы изменить данные, извлеченные из мета-файла пользователя closedpostboxes_post
:
add_filter( 'get_user_option_closedpostboxes_post',
function ( $result, $option, $user )
{
$post_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT );
$newresult = get_user_option( 'closedpostboxes_post_'. $post_id , $user->ID );
return ( $newresult ) ? $newresult : $result;
}
, 10, 3 );
Мы также могли бы подумать о случае, когда нет доступных сообщений на основе closedpostboxes_post_{post_id}
. Таким образом, он будет использовать последние сохраненные настройки из closedpostboxes_post
. Может быть, вы хотели бы чтобы все это было открыто или закрыто, в этом случае по умолчанию. Было бы легко изменить это поведение.
Для других пользовательских типов сообщений мы можем использовать соответствующий крючок closedpostboxes_{post_type}
.
То же самое должно быть возможно для упорядочивания и скрытия метабоксов с metaboxhidden_{post_type}
и meta-box-order_{post_data}
пользовательской метой.
ps: извините за этот слишком длинный ответ на выходные, так как они всегда должны быть короткими и веселыми ;-)
Как указал биргир в своем ответе , WordPress использует AJAX для обновления статуса метабоксов, а данные, передаваемые в запросе AJAX, не включают идентификатор сообщения, и это затрудняет обновление статуса ящиков на основе каждого сообщения.
Как только я обнаружил, что действие AJAX, используемое WordPress, является 'closed-postboxes'
, я искал эту строку в папке admin js, чтобы узнать, как WordPress выполняет запрос AJAX.
Я обнаружил, что это происходит на postbox.js
на линии #118.
Это выглядит так:
save_state : function(page) {
var closed = $('.postbox').filter('.closed').map(function() {
return this.id;
}).get().join(',');
var hidden = $('.postbox').filter(':hidden').map(function() {
return this.id;
}).get().join(',');
$.post(ajaxurl, {
action: 'closed-postboxes',
closed: closed,
hidden: hidden,
closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
page: page
});
}
По сути, WordPress просматривает элементы DOM с классами "почтовый ящик" и "закрытый" и создает список их идентификаторов, разделенных запятыми. То же самое делается для скрытых элементов DOM с классом "почтовый ящик".
Итак, я подумал: я могу создать поддельный метабокс, который имеет правильные классы и который скрыт, установив его идентификатор, содержащий идентификатор записи, и таким образом я могу получить его в запросе AJAX.
Вот что у меня есть сделано:
add_action( 'dbx_post_sidebar', function() {
global $post;
if ( $post->post_type === 'mycpt' ) {
$id = $post->ID;
$f = '<span id="fakebox_pid_%d" class="postbox closed" style="display:none;"></span>';
printf( $f, $id );
}
});
Таким образом, я создал метабокс, который всегда закрыт и всегда скрыт, поэтому WordPress отправит свой идентификатор в виде $_POST
var в запросе AJAX, и как только поддельный идентификатор ящика будет содержать идентификатор сообщения предсказуемым образом, я смогу распознать сообщение.
После этого я посмотрел, как WordPress выполняет задачу AJAX.
В admin-ajax.php
в строке 72 WordPress подключает 'wp_ajax_closed-postboxes'
с приоритетом 1.
Итак, чтобы действовать до WordPress, я мог бы подключить то же действие с приоритетом 0.
add_action( 'wp_ajax_closed-postboxes', function() {
// check if we are in right post type: WordPress passes it in 'page' post var
$page = filter_input( INPUT_POST, 'page', FILTER_SANITIZE_STRING );
if ( $page !== 'mycpt' ) return;
// get post data
$data = filter_input_array( INPUT_POST, array(
'closed' => array( 'filter' => FILTER_SANITIZE_STRING ),
'hidden' => array( 'filter' => FILTER_SANITIZE_STRING )
) );
// search among closed boxes for the "fake" one, and return if not found
$look_for_fake = array_filter( explode( ',', $data[ 'closed' ] ), function( $id ) {
return strpos( $id, 'fakebox_pid_' ) === 0;
} );
if ( empty( $look_for_fake ) ) return;
$post_id = str_replace( 'fakebox_pid_', '', $look_for_fake[0] );
$user_id = get_current_user_id();
// remove fake id from values
$closed = implode(',', array_diff( explode(',', $data['closed'] ), $look_for_fake ) );
$hidden = implode(',', array_diff( explode(',', $data['hidden'] ), $look_for_fake ) );
// save metabox status on a per-post and per-user basis in a post meta
update_post_meta( $post_id, "_mycpt_closed_boxes_{user_id}", $closed );
update_post_meta( $post_id, "_mycpt_hidden_boxes_{user_id}", $hidden );
}, 0 );
Сохранение данных в мета-посте позволило отфильтровать get_user_option_closedpostboxes_mycpt
и get_user_option_metaboxhidden_mycpt
(оба варианта get_user_option_{$option}
фильтр), чтобы принудительно загрузить параметры WordPress из мета-записи:
add_filter( 'get_user_option_closedpostboxes_mycpt', function ( $result, $key, $user ) {
global $post;
$meta = get_post_meta( $post->ID, "_mycpt_closed_boxes_{$user->ID}", TRUE );
if ( ! empty( $meta ) ) {
$result = $meta;
}
return $result;
}, 10, 3 );
И
add_filter( 'get_user_option_metaboxhidden_mycpt', function ( $result, $key, $user ) {
global $post;
$meta = get_post_meta( $post->ID, "_mycpt_hidden_boxes_{$user->ID}", TRUE );
if ( ! empty( $meta ) ) {
$result = $meta;
}
return $result;
}, 10, 3 );