Bagaimana cara membuat status metabox terbuka / tertutup dan tersembunyi / ditampilkan disimpan pada basis per posting?

9

Masalah saya sebenarnya agak rumit, jadi saya akan mencoba di sini untuk abstrak dan menjaganya tetap sederhana.

Saya sedang mengerjakan aplikasi khusus berdasarkan WordPress. Saya mendaftarkan jenis posting khusus, sebut saja "orang" tempat saya menyimpan informasi tentang ... orang.

CPT hanya mendukung judul posting dan kolom standar konten posting, tetapi ada beberapa metabox untuk menyimpan informasi orang (anggap aplikasi saya sebagai buku alamat).

Jadi ada metabox untuk menyimpan info pribadi, satu untuk menyimpan info jejaring sosial, yang lain untuk menyimpan info terkait pekerjaan, yaitu jika orang itu bagi saya pelanggan, pemasok, jika kita memiliki kredit atau debit ...

Saya menyederhanakan di sini, tetapi ada sejumlah metabox yang konsisten, katakanlah 12.

Masalah saya adalah, beberapa orang yang ingin saya simpan info hanyalah kontak acak, dan saya ingin menyimpan hanya info pribadi, yang lain adalah teman dan saya ingin menyimpan info pribadi dan info jejaring sosial, lainnya adalah pelanggan atau pemasok dan saya ingin menyimpan info terkait pekerjaan.

Jika ketika mengedit posting saya menyembunyikan (melalui menu opsi layar ) atau menutup metabox yang tidak saya butuhkan, ketika saya membuka posting lain di mana saya membutuhkannya saya harus menunjukkan atau membukanya lagi. Itu karena posisi / status / pesanan metabox disimpan berdasarkan per pengguna sebagai metadata pengguna .

Jika Anda membayangkan di beberapa posting saya perlu 2 metabox, di beberapa 10 dan di 5, Anda mengerti itu menjengkelkan karena menjaga semua yang ditampilkan / terbuka membuat layar edit rendah dapat diakses (scrollbar sepertinya tak ada habisnya), dan kadang-kadang info yang saya cari adalah di akhir halaman setelah sekelompok metabox tanpa info ...

Pertanyaan:

Apakah mungkin untuk menyimpan posisi / status / pesanan metabox berdasarkan per-posting untuk jenis posting tertentu?


PS: Saya tahu beberapa js / jQuery dapat mengatasi masalah ini, tetapi jika memungkinkan saya akan menghindari solusi javascript.

gmazzap
sumber

Jawaban:

8

Masalah utama:

Masalah utama di sini adalah bahwa dalam panggilan ajax penutupan , sembunyi , dan pemesanan , tidak ada ID pos yang dikirim dengan muatan. Berikut adalah dua contoh data formulir:

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

Kita bisa menyiasatinya dengan menggunakan panggilan ajax khusus.

Kami tentu saja bisa menghubungkan ke save_posthook dan memodifikasi data setiap kali posting disimpan. Tapi itu bukan pengalaman UI normal, jadi itu tidak dipertimbangkan di sini

Ada solusi non-elegan lain yang tersedia dengan PHP, dijelaskan di bawah ini:

Solusi Non Javascript:

Pertanyaannya adalah di mana menyimpan data? Sebagai data meta pengguna , poskan data meta atau mungkin dalam tabel khusus?

Di sini kita menyimpannya sebagai meta data pengguna dan mengambil penutupan dari pos meta kotak sebagai contoh.

Ketika nilai closedpostboxes_postmeta diperbarui, kami menyimpannya ke dalam closedpostboxes_post_{post_id}nilai meta juga.

Kemudian kami membajak pengambilan closedpostboxes_postuntuk menimpanya dengan nilai meta yang sesuai berdasarkan id pengguna dan id posting.

a) Memperbarui selama closed-postboxesaksi ajax:

Kami dapat mengambil ID posting, melalui wp_get_referer()dan kemudian menggunakan url_to_postid()fungsi praktis . Saya pertama kali tahu tentang fungsi "lucu" ini setelah membaca jawaban dari @s_ha_dum , beberapa bulan yang lalu ;-) Sayangnya fungsi tersebut tidak mengenali ?post=123variabel GET, tetapi kita bisa melakukan sedikit trik dengan hanya mengubahnya p=123untuk mengelilinginya.

Kami dapat menghubungkan updated_user_meta, yang dipecat setelah data meta pengguna untuk closedpostboxes_posttelah diperbarui:

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) Mengambil data:

Kami dapat menghubungkan ke get_user_option_closedpostboxes_posthook untuk memodifikasi data yang diambil dari closedpostboxes_postmeta pengguna:

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

Kami mungkin juga ingin memikirkan kasus di mana tidak ada posting berbasis closedpostboxes_post_{post_id}tersedia. Jadi itu akan menggunakan pengaturan yang disimpan terakhir dari closedpostboxes_post. Mungkin Anda ingin semuanya terbuka atau tertutup, dalam kasus default itu. Akan mudah untuk mengubah perilaku ini.

Untuk jenis pos khusus lainnya, kami dapat menggunakan closedpostboxes_{post_type}kait yang sesuai .

Hal yang sama harus dimungkinkan untuk pemesanan dan penyembunyian metabox dengan meta metaboxhidden_{post_type}dan meta-box-order_{post_data}pengguna.

ps: maaf untuk jawaban akhir pekan yang terlalu panjang ini, karena harus selalu pendek & riang ;-)

birgire
sumber
+1 Luar Biasa. T / J untuk jawaban panjang, saya tidak akan mengharapkan yang pendek. Sejujurnya saya tidak mengharapkan apa pun di akhir pekan :) Dua hal yang sangat saya sukai: ide pertama untuk menyimpan data berdasarkan per-pengguna dan per-posting: ide saya adalah untuk menyimpan di posting meta, tetapi dengan cara itu semua pengguna akan memiliki status yang sama. 2 ide yang digunakan 'get_user_option_*_post'untuk membuat WP mengenali data khusus. Hanya saya pikir saya tidak terlalu suka penggunaan wp_get_refereritu pada $_SERVERvar yang tidak benar-benar dapat diandalkan tapi saya pikir saya punya ide untuk mengatasi "masalah utama";)
gmazzap
Terima kasih, saya kira itu tergantung pada jumlah pengguna dan posting di mana sebaiknya menyimpan data. Mungkin data ini harus memiliki beberapa TTL dan dihapus misalnya sebulan sekali? Ya saya setuju dengan Anda mengenai wp_get_referer()metode ini, itu sebabnya saya menyebutnya solusi PHP yang tidak elegan ;-) Saya pertama kali berpikir untuk menyimpan id posting saat ini untuk setiap pengguna, tetapi itu tidak berfungsi jika pengguna mengedit dua atau lebih posting di browser. Nantikan mendengar ide Anda tentang "masalah utama" Nikmati akhir pekan ;-)
birgire
Setelah 43 hari, upvote mengingatkan saya untuk menjawab ini. Sekali lagi terima kasih atas jawaban Anda.
gmazzap
6

Seperti yang ditunjukkan oleh birgire dalam jawabannya , WordPress menggunakan AJAX untuk memperbarui status metabox dan data yang diteruskan dalam permintaan AJAX tidak termasuk pos id, dan itu membuatnya sulit untuk memperbarui status kotak berdasarkan basis per-posting.

Setelah saya menemukan tindakan AJAX yang digunakan oleh WordPress 'closed-postboxes', saya mencari string ini di folder admin js untuk menemukan bagaimana WordPress membuat permintaan AJAX.

Saya menemukan itu terjadi pada postbox.jsbaris # 118 .

Sepertinya:

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

Pada dasarnya, WordPress melihat item DOM dengan kelas 'kotak pos' dan kelas 'tertutup' dan membuat daftar ID yang dipisahkan koma. Hal yang sama dilakukan untuk item DOM tersembunyi dengan kelas 'kotak pos'.

Jadi, pikiran saya adalah: Saya dapat membuat metabox palsu yang memiliki kelas yang tepat dan yang disembunyikan, mengatur id-nya untuk berisi ID posting, dan dengan cara ini saya dapat mengambilnya dalam permintaan AJAX.

Inilah yang telah saya lakukan:

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

Dengan cara ini saya membuat metabox yang selalu tertutup dan selalu disembunyikan, jadi WordPress akan mengirimkan ID-nya sebagai $_POSTvar dalam permintaan AJAX, dan begitu id kotak palsu berisi ID pos dengan cara yang dapat diprediksi, saya dapat mengenali pos tersebut.

Setelah itu saya melihat bagaimana WordPress melakukan tugas AJAX.

Pada admin-ajax.phpbaris 72 , WordPress terhubung 'wp_ajax_closed-postboxes'dengan prioritas 1.

Jadi, untuk bertindak sebelum WordPress, saya dapat mengaitkan tindakan yang sama dengan prioritas 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 );

Setelah data disimpan dalam meta pos memungkinkan untuk memfilter get_user_option_closedpostboxes_mycptdan get_user_option_metaboxhidden_mycpt(keduanya variasi get_user_option_{$option}filter) untuk memaksa opsi pemuatan WordPress dari meta pos:

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

dan

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
sumber
Apa ide bagus menggunakan metabox tersembunyi dengan info yang relevan +1
birgire
terima kasih @birgire dan terima kasih lagi untuk nilai A Anda, ide untuk menyimpan data berdasarkan per-pengguna dan per-posting adalah milik Anda :)
gmazzap