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


Моя настоящая проблема немного сложна, поэтому я постараюсь здесь абстрагироваться от нее и сделать ее простой.

Я работаю над пользовательским приложением на основе WordPress. Я зарегистрировал пользовательский тип записи, назовем его "люди", где я храню информацию о... людях.

CPT поддерживает только поля по умолчанию "Заголовок публикации" и "содержимое публикации", но есть несколько метабоксов для хранения информации о пользователях (например, мое приложение в качестве адресной книги).

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

Я здесь упростил, но существует постоянное количество метабоксов, скажем, 12.

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

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

Если вы представляете, что в некоторых сообщениях мне нужно 2 метабокса, в некоторых 10 и в некоторых 5, вы понимаете, что это раздражает, потому что все они показаны/открыты сделайте экран редактирования малодоступным (полоса прокрутки кажется бесконечной), и иногда информация, которую я ищу, находится в конце страницы после нескольких метабоксов без информации...

Вопрос:

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


PS: Я знаю, что некоторые js/jQuery могут решить эту проблему, но, если возможно, я бы избегал решений javascript.

 9
Author: gmazzap, 2014-09-13

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: извините за этот слишком длинный ответ на выходные, так как они всегда должны быть короткими и веселыми ;-)

 8
Author: birgire, 2017-04-13 12:37:35

Как указал биргир в своем ответе , 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 );
 6
Author: gmazzap, 2017-04-13 12:37:54