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

9

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

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

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

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

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

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

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

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

Вопрос:

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


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

Gmazzap
источник

Ответы:

8

Главная проблема:

Основная проблема здесь заключается в том , что при закрытии , скрытии и упорядочивании вызовов 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чтобы переопределить ее с соответствующим мета-значением на основе идентификатора пользователя и идентификатора поста.

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

birgire
источник
Отлично +1. N / P для длинного ответа, я не ожидал бы коротких. Честно говоря, я ничего не ожидал на выходных :) Две вещи мне очень понравились: во-первых, идея хранить данные для каждого пользователя и для каждого поста: моя идея заключалась в том, чтобы хранить в пост-мета, но таким образом все пользователи будут иметь такой же статус. Во-вторых, идея использовать 'get_user_option_*_post'WP для распознавания пользовательских данных. Только думаю, что мне не очень нравится, это использование wp_get_refererдействительно на $_SERVERvar, которое не очень надежно, но я думаю, что у меня есть идея преодолеть «главную проблему»;)
gmazzap
Спасибо, я думаю, это зависит от количества пользователей и постов, где было бы лучше хранить данные. Может быть, эти данные должны иметь некоторые TTL и стираться, например, раз в месяц? Да, я согласен с вами относительно wp_get_referer()метода, поэтому я назвал его не элегантным решением PHP ;-) Сначала я подумал о сохранении текущего идентификатора записи для каждого пользователя, но это не работает, если пользователь редактирует два или более посты в браузере.
Будем рады
После 43 дней я вспомнил, что я ответил на это. Еще раз спасибо за ваш ответ.
gmazzap
6

Как отметил birgire в своем ответе , 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 с классом 'postbox' и классом 'closed' и создает разделенный запятыми список их идентификаторов. То же самое сделано для скрытых элементов DOM с классом 'postbox'.

Итак, я подумал: я могу создать фальшивый метабокс, который имеет правильные классы и который скрыт, установив его идентификатор, содержащий идентификатор публикации, и таким образом я могу получить его в запросе 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 отправит свой идентификатор в виде $_POSTvar в запросе 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 );
Gmazzap
источник
Какая отличная идея, используя скрытый метабокс с соответствующей информацией +1
birgire
спасибо @birgire и еще раз спасибо за вашу A, идея сохранения данных как для каждого пользователя, так и для каждого поста принадлежит вам :)
gmazzap