Jak zapisać status otwartych / zamkniętych i ukrytych / pokazywanych metaboksów dla poszczególnych postów?

9

Mój prawdziwy problem jest nieco złożony, więc postaram się tutaj go streścić i uprościć.

Pracuję nad niestandardową aplikacją opartą na WordPress. Zarejestrowałem niestandardowy typ postu, nazwijmy go „osobami”, w których przechowuję informacje o ... osobach.

CPT obsługuje tylko domyślne pola tytułów postów i treści postów, ale są też metaboksy do przechowywania informacji o osobach (myślę, że moja aplikacja to książka adresowa).

Tak więc istnieje metaboks do przechowywania danych osobowych, jeden do przechowywania informacji w sieciach społecznościowych, inny do przechowywania informacji związanych z pracą, tj. Jeśli ta osoba jest dla mnie klientem, dostawcą, jeśli mamy kredyty lub obciążenia ...

Upraszczam tutaj, ale istnieje spójna liczba metaboksów, powiedzmy 12.

Mój problem polega na tym, że niektóre osoby, dla których chcę przechowywać informacje, są przypadkowymi kontaktami i chcę przechowywać tylko dane osobowe, inni są przyjaciółmi i chcę przechowywać dane osobowe i informacje z sieci społecznościowych, inni są klientami lub dostawcami, a ja chcesz przechowywać informacje związane z pracą.

Jeśli podczas edytowania postu chowam się (za pomocą menu opcji ekranu ) lub zamykam niepotrzebny metaboks, kiedy otwieram inny post, w którym go potrzebuję, muszę go pokazać lub ponownie otworzyć. Jest tak, ponieważ pozycja / status / kolejność metaboksów są zapisywane dla poszczególnych użytkowników jako metadane użytkownika .

Jeśli wyobrażasz sobie, że w niektórych postach potrzebuję 2 metaboksów, w niektórych 10 i 5, rozumiesz, że to denerwujące, ponieważ utrzymywanie ich wszystkich wyświetlanych / otwartych sprawia, że ​​ekran edycji jest niski (pasek przewijania wydaje się nie mieć końca), a czasami informacje, których szukam, to na końcu strony po grupie metaboksów bez informacji ...

Pytanie:

Czy możliwe jest zapisywanie pozycji / statusu / zamówienia metaboksów dla poszczególnych postów?


PS: Wiem, że niektóre js / jQuery mogą rozwiązać problem, ale jeśli to możliwe, unikałbym javascript.

gmazzap
źródło

Odpowiedzi:

8

Główny problem:

Głównym problemem jest to, że w closing- , hiding- i ordering- wywołania AJAX, nie ma ID poczta wysłana z ładunkiem. Oto dwa przykłady danych formularza:

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]:

Możemy to obejść, używając innego niestandardowego wywołania ajax.

Oczywiście możemy po prostu podpiąć się do save_posthaka i zmodyfikować dane za każdym razem, gdy post jest zapisywany. Ale to nie jest normalne doświadczenie w interfejsie użytkownika, więc nie jest to tutaj rozważane

Istnieje inne nie eleganckie rozwiązanie dostępne w PHP, opisane poniżej:

Rozwiązanie inne niż JavaScript:

Pytanie brzmi, gdzie przechowywać dane? Jako metadane użytkownika , publikuj metadane, a może w niestandardowej tabeli?

Tutaj możemy przechowywać je jako dane meta użytkowników i wziąć zamykanie z pocztowych skrzynek meta jako przykład.

Gdy closedpostboxes_postwartość meta jest aktualizowana, zapisujemy ją również w closedpostboxes_post_{post_id}meta wartości.

Następnie przejmujemy pobieranie, closedpostboxes_postaby zastąpić je odpowiednią wartością meta na podstawie identyfikatora użytkownika i identyfikatora postu.

a) Aktualizacja podczas closed-postboxesakcji ajax:

Możemy pobrać identyfikator posta poprzez, wp_get_referer()a następnie użyć przydatnej url_to_postid()funkcji. Po raz pierwszy wiedziałem o tej „zabawnej” funkcji po przeczytaniu odpowiedzi z @s_ha_dum , kilka miesięcy temu ;-) Niestety funkcja nie rozpoznaje ?post=123zmiennych GET, ale możemy zrobić małą sztuczkę, zmieniając ją, aby p=123obejść ten problem.

Możemy się podłączyć updated_user_meta, to jest uruchamiane zaraz po closedpostboxes_postaktualizacji meta danych użytkownika dla :

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 );

b) Pobieranie danych:

Możemy podłączyć się do get_user_option_closedpostboxes_posthaka, aby zmodyfikować dane pobrane z closedpostboxes_postmeta użytkownika:

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 );

Możemy również zastanowić się nad przypadkiem, w którym nie ma closedpostboxes_post_{post_id}dostępnych postów . Więc użyje ostatnio zapisanych ustawień z closedpostboxes_post. Być może chciałbyś mieć to wszystko otwarte lub wszystkie zamknięte, w tym domyślnym przypadku. Łatwo byłoby zmodyfikować to zachowanie.

W przypadku innych niestandardowych typów słupków możemy użyć odpowiedniego closedpostboxes_{post_type}haka.

To samo powinno być możliwe w przypadku zamawiania i ukrywania metaboksów w meta metaboxhidden_{post_type}i meta-box-order_{post_data}użytkownika.

ps: przepraszam za tę zbyt długą weekendową odpowiedź, ponieważ zawsze powinny być krótkie i wesołe ;-)

birgire
źródło
Świetna +1. N / P dla długiej odpowiedzi, nie spodziewałbym się krótkich. Szczerze mówiąc, nie spodziewałem się żadnych w weekend :) Dwie rzeczy bardzo mi się podobały: 1. pomysł na przechowywanie danych dla poszczególnych użytkowników i na post: moim pomysłem było przechowywanie w post meta, ale w ten sposób wszystko użytkownicy będą mieli ten sam status. 2. pomysł, 'get_user_option_*_post'aby WP rozpoznał dane niestandardowe. Myślę tylko, że nie podoba mi się zbytnio korzystanie z wp_get_referertego na $_SERVERvar, co nie jest tak naprawdę niezawodne, ale myślę, że mam pomysł na rozwiązanie „głównego problemu”;)
gmazzap
Dzięki, myślę, że zależy to od liczby użytkowników i postów, w których najlepiej byłoby przechowywać dane. Może te dane powinny zawierać TTL i zostać usunięte np. Raz w miesiącu? Tak, zgadzam się z tobą co do wp_get_referer()metody, dlatego nazwałem to nie eleganckim rozwiązaniem PHP ;-) Najpierw pomyślałem o przechowywaniu bieżącego identyfikatora postu dla każdego użytkownika, ale to nie działa, jeśli użytkownik edytuje dwa lub więcej posty w przeglądarce. Czekamy na wiadomość na temat twojego „głównego problemu”. Ciesz się weekendem ;-)
birgire,
Po 43 dniach upvote przypomniał mi, żebym odpowiedział na to pytanie. Jeszcze raz dziękuję za odpowiedź.
gmazzap
6

Jak zauważył birgire w swojej odpowiedzi , WordPress używa AJAX do aktualizacji statusu metaboksów, a dane przekazywane w żądaniu AJAX nie zawierają identyfikatora postu, co utrudnia aktualizację statusu skrzynek na post.

Po znalezieniu akcji AJAX używanej przez WordPress 'closed-postboxes'szukałem tego ciągu w folderze admin js, aby dowiedzieć się, jak WordPress wysyła żądanie AJAX.

Odkryłem, że dzieje się to postbox.jsna linii nr 118 .

Wygląda to tak:

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
  });
}

Zasadniczo WordPress patrzy na elementy DOM z klasą „postbox” i klasą „zamkniętą” i tworzy listę identyfikatorów oddzieloną przecinkami. To samo dotyczy ukrytych elementów DOM z klasą „postbox”.

Tak więc, pomyślałem: mogę stworzyć fałszywy metaboks, który ma odpowiednie klasy i który jest ukryty, ustawiając jego identyfikator na identyfikator posta, i w ten sposób mogę go pobrać w żądaniu AJAX.

Oto co zrobiłem:

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 );
    }
});

W ten sposób stworzyłem metaboks, który jest zawsze zamknięty i zawsze ukryty, więc WordPress wyśle ​​swój identyfikator jako $_POSTvar w żądaniu AJAX, a gdy fałszywy identyfikator skrzynki będzie zawierał ID postu w przewidywalny sposób, będę w stanie rozpoznać post.

Potem spojrzałem na to, jak WordPress wykonuje zadanie AJAX.

W admin-ajax.phplinii 72 WordPress przechwytuje 'wp_ajax_closed-postboxes'z priorytetem 1.

Tak więc, aby działać przed WordPress, mógłbym zaczepić tę samą akcję o priorytecie 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 );

Zapisanie danych w meta postie umożliwiło filtrowanie get_user_option_closedpostboxes_mycpti get_user_option_metaboxhidden_mycpt(obie odmiany get_user_option_{$option}filtra) wymuszenie opcji ładowania WordPress z meta postu:

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 );

i

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
źródło
Cóż za świetny pomysł, używając ukrytego metaboksu z odpowiednimi informacjami +1
birgire
dzięki @birgire i jeszcze raz dziękuję za twoją literę A, pomysł zapisywania danych zarówno na użytkownika, jak i na pocztę należy do Ciebie :)
gmazzap