Come salvare lo stato dei metabox aperti / chiusi e nascosti / mostrati per post?


9

Il mio vero problema è un po 'complesso, quindi proverò qui ad astrarlo e a mantenerlo semplice.

Sto lavorando su un'app personalizzata basata su WordPress. Ho registrato un tipo di post personalizzato, chiamiamolo "persone" in cui memorizzo informazioni su ... persone.

CPT supporta solo i campi predefiniti per post titolo e post contenuto, ma esistono alcuni metabox per memorizzare le informazioni sulla persona (pensa alla mia app come una rubrica).

Quindi esiste un metabox per memorizzare informazioni personali, uno per archiviare informazioni sui social network, un altro per archiviare informazioni relative al lavoro, cioè se quella persona è per me un cliente, un fornitore, se abbiamo crediti o debiti ...

Ho semplificato qui, ma ci sono una quantità consistente di metabox, diciamo 12.

Il mio problema è che, alcune persone per le quali voglio memorizzare informazioni sono solo contatti casuali, e voglio memorizzare solo informazioni personali, altre sono amici e voglio memorizzare informazioni personali e informazioni sui social network, altre sono clienti o fornitori e io desidera memorizzare informazioni relative al lavoro.

Se durante la modifica di un post nascondo (tramite il menu delle opzioni dello schermo ) o chiudo qualsiasi metabox di cui non ho bisogno, quando apro un altro post dove ne ho bisogno devo mostrarli o aprirli di nuovo. Questo perché i metabox posizione / stato / ordine vengono salvati su base per utente come metadati dell'utente .

Se immagini in alcuni post ho bisogno di 2 metabox, in alcuni 10 e in alcuni 5, capisci che è fastidioso perché tenere tutti mostrati / aperti rende la schermata di modifica poco accessibile (la barra di scorrimento sembra infinita), e talvolta le informazioni che cerco sono alla fine della pagina dopo un mucchio di metabox senza informazioni ...

Domanda:

È possibile salvare la posizione / lo stato / l'ordine dei metabox su una base per posta per un tipo di posta specifico?


PS: So che alcuni js / jQuery possono risolvere il problema, ma se possibile eviterei le soluzioni javascript.

Risposte:


8

Il problema principale:

Il problema principale qui è che nelle chiamate ajax di chiusura , nascondimento e ordinazione , non è stato inviato alcun ID post con il payload. Ecco due esempi di dati di modulo:

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

Potremmo aggirare questo problema utilizzando un'altra chiamata Ajax personalizzata.

Ovviamente potremmo semplicemente agganciarci save_poste modificare i dati ogni volta che il post viene salvato. Ma questa non è la normale esperienza dell'interfaccia utente, quindi non è da considerare qui

C'è un'altra soluzione non elegante disponibile con PHP, descritta di seguito:

Una soluzione non Javascript:

La domanda è dove archiviare i dati? Come metadati dell'utente , pubblicare metadati o magari in una tabella personalizzata?

Qui abbiamo memorizzare come meta dati utente e prendiamo la chiusura di pubblicare scatole meta come esempio.

Quando il closedpostboxes_postmeta-valore viene aggiornato, lo salviamo anche nel closedpostboxes_post_{post_id}meta-valore.

Quindi dirottiamo il recupero di closedpostboxes_postper sovrascriverlo con il corrispondente meta valore basato sull'ID utente e sull'ID post.

a) Aggiornamento durante l' closed-postboxesazione ajax:

Possiamo recuperare l'ID del post, tramite wp_get_referer()e quindi utilizzare la comoda url_to_postid()funzione. Ho saputo per la prima volta di questa funzione "divertente" dopo aver letto la risposta di @s_ha_dum , pochi mesi fa ;-) Purtroppo la funzione non riconosce le ?post=123variabili GET, ma possiamo fare un piccolo trucco semplicemente cambiandola per aggirarlap=123 .

Possiamo collegarci updated_user_meta, che viene attivato subito dopo l' closedpostboxes_postaggiornamento dei metadati dell'utente :

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) Recupero dati:

Siamo in grado di agganciare il get_user_option_closedpostboxes_postgancio per modificare i dati recuperati dal closedpostboxes_postmeta utente:

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

Potremmo anche pensare al caso in cui non ci sono post closedpostboxes_post_{post_id}disponibili. Quindi utilizzerà le ultime impostazioni salvate da closedpostboxes_post. Forse vorresti avere tutto aperto o tutto chiuso, in quel caso predefinito. Sarebbe facile modificare questo comportamento.

Per altri tipi di post personalizzati possiamo usare l' closedpostboxes_{post_type}hook corrispondente .

Lo stesso dovrebbe essere possibile per l' ordinamento e il nascondimento di metabox con il meta metaboxhidden_{post_type}e l' meta-box-order_{post_data}utente.

ps: scusami per questa risposta del weekend troppo lunga, dal momento che dovrebbero essere sempre brevi e allegri ;-)


Ottimo +1. N / P per una risposta lunga, non mi aspetterei quelli brevi. A dire il vero non me l'aspettavo per il fine settimana :) Due cose che mi sono piaciute molto: 1 ° l'idea di archiviare i dati per utente e per post: la mia idea era quella di archiviare in post meta, ma in quel modo tutto gli utenti avranno lo stesso stato. Secondo l'idea da utilizzare 'get_user_option_*_post'per far riconoscere a WP dati personalizzati. L'unica cosa che non mi piace troppo è l'uso di wp_get_refererquesto su $_SERVERVar, che non è molto affidabile, ma penso di avere un'idea per superare il "problema principale";)
gmazzap

Grazie, immagino che dipenda dal numero di utenti e post in cui sarebbe meglio archiviare i dati. Forse questi dati dovrebbero avere un po 'di TTL ed essere cancellati, ad esempio una volta al mese? Sì, sono d'accordo con te per quanto riguarda il wp_get_referer()metodo, ecco perché l'ho chiamato una soluzione PHP non elegante ;-) Ho pensato prima di memorizzare l'ID post corrente per ogni utente, ma non funziona se un utente sta modificando due o più post nel browser. Non vedo l'ora di conoscere la tua idea sul "problema principale" Goditi il ​​weekend ;-)
birgire,

Dopo 43 giorni un voto mi ricorda di rispondere. Grazie ancora per la tua risposta.
gmazzap

6

Come sottolineato da Birgire nella sua risposta , WordPress utilizza AJAX per aggiornare lo stato dei metabox e i dati trasmessi nella richiesta AJAX non includono l'id post e ciò rende difficile aggiornare lo stato delle caselle in base al post.

Una volta trovata l'azione AJAX utilizzata da WordPress 'closed-postboxes', ho cercato questa stringa nella cartella admin js per trovare come WordPress effettua la richiesta AJAX.

Ho scoperto che succede sulla postbox.jslinea # 118 .

Sembra così:

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

In sostanza, WordPress esamina gli elementi DOM con classe 'casella postale' e classe 'chiuso' e crea un elenco separato da virgole dei loro ID. Lo stesso vale per gli oggetti DOM nascosti con classe "casella postale".

Quindi, il mio pensiero era: posso creare un metabox falso che ha le classi giuste e che è nascosto, impostando il suo id per contenere l'ID post e in questo modo posso recuperarlo in una richiesta AJAX.

Questo è quello che ho fatto:

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

In questo modo ho creato un metabox che è sempre chiuso e sempre nascosto, quindi WordPress invierà il suo ID come $_POSTvar nella richiesta AJAX, e una volta che l'id box falso contiene l'ID post in modo prevedibile, sono in grado di riconoscere il post.

Dopo di che ho visto come WordPress esegue l'attività AJAX.

Nella admin-ajax.phpriga 72 , WordPress si aggancia 'wp_ajax_closed-postboxes'con la priorità 1.

Quindi, per agire prima di WordPress, potrei agganciare la stessa azione con priorità 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 );

Avere i dati salvati in un post meta ha reso possibile filtrare get_user_option_closedpostboxes_mycpte get_user_option_metaboxhidden_mycpt(entrambe le varianti del get_user_option_{$option}filtro) per forzare le opzioni di caricamento di WordPress da post meta:

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

e

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

Che grande idea usare un metabox nascosto con le relative informazioni +1
birgire,

grazie @birgire e grazie ancora per la tua A, l'idea di salvare i dati sia per utente che per posta è tutta tua :)
gmazzap
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.