web-dev-qa-db-fra.com

Comment faire en sorte que le statut des métaboxes ouverts/fermés et masqués/affichés soit enregistré poste par poste?

Mon vrai problème est un peu complexe, je vais donc essayer ici de le résumer et de le garder simple.

Je travaille sur une application personnalisée basée sur WordPress. J'ai enregistré un type de message personnalisé, appelons-le "personnes" où je stocke des informations sur ... les gens.

Le CPT ne prend en charge que les champs par défaut du titre et du contenu du message, mais il existe des métaboxes pour stocker les informations personnelles (pensez à mon application comme un carnet d'adresses).

Il existe donc une metabox pour stocker des informations personnelles, une pour stocker des informations sur les réseaux sociaux, une autre pour stocker des informations relatives au travail, c’est-à-dire si cette personne est pour moi un client, un fournisseur, si nous avons des crédits ou des débits ...

J'ai simplifié ici, mais il y a une quantité constante de métaboxes, disons 12.

Mon problème est que, certaines personnes pour lesquelles je veux stocker des informations ne sont que des contacts aléatoires et que je ne souhaite stocker que des informations personnelles, d’autres sont des amis et que je souhaite stocker des informations personnelles et des informations sur les réseaux sociaux, d’autres sont des clients ou des fournisseurs et moi-même. vouloir stocker des informations relatives au travail.

Si, lors de l'édition d'un message, je cache (via le menu d'options screen ) ou ferme toute metabox dont je n'ai pas besoin, lorsque j'ouvre un autre message où j'en ai besoin, je dois le montrer ou le rouvrir. Cela parce que la position/le statut/la commande des métaboxes sont sauvegardés par utilisateur en tant que métadonnées utilisateur .

Si vous pensez que dans certains articles, j'ai besoin de 2 métaboxes, de 10 et de 5, vous comprenez que c'est embêtant, car si tous les éléments sont affichés/ouverts, l'écran de montage est accessible (barre de défilement semble infinie), et parfois l'information que je recherche est à la fin de la page après un tas de métaboxes sans aucune information ...

Question:

Est-il possible de sauvegarder la position/le statut/la commande de metaboxes sur une base individuelle pour un type de publication spécifique?


PS: Je sais que certains js/jQuery peuvent résoudre le problème, mais si possible, je voudrais éviter les solutions javascript.

9
gmazzap

Comme indiqué par birgire in sa réponse , WordPress utilise AJAX pour mettre à jour le statut des métaboxes et les données transmises dans la demande AJAX n'incluant pas post id , ce qui rend difficile la mise à jour du statut des boîtes par poste.

Une fois que j'ai trouvé que l'action AJAX utilisée par WordPress est 'closed-postboxes', j'ai cherché cette chaîne dans le dossier admin js pour savoir comment WordPress effectue la demande AJAX.

J'ai trouvé que cela se passe sur postbox.js à la ligne 118 .

Cela ressemble à ceci:

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

Pour l'essentiel, WordPress examine les éléments DOM avec les classes 'postbox' et 'closed' et crée une liste de leurs identifiants séparés par des virgules. La même chose est faite pour les éléments DOM masqués avec la classe 'postbox'.

Donc, ma pensée était: je peux créer une metabox fake qui a les bonnes classes et qui est cachée, en définissant son id pour contenir post ID, et de cette manière je peux le récupérer dans un AJAX demande.

Voici ce que j'ai fait:

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

De cette façon, j'ai créé une metabox qui est toujours fermée et toujours masquée. WordPress envoie donc son identifiant sous la forme $_POST var dans la requête AJAX, et une fois que l'identifiant de fake box contient l'identifiant de publication de manière prévisible, I suis capable de reconnaître la poste.

Après cela, j’ai examiné la manière dont WordPress effectue la tâche AJAX.

Dans admin-ajax.php à la ligne 72 , WordPress raccroche 'wp_ajax_closed-postboxes' avec la priorité 1.

Donc, pour agir avant WordPress, je pourrais accrocher la même action avec la 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 );

Avoir des données sauvegardées dans un post meta permettait de filtrer get_user_option_closedpostboxes_mycpt et get_user_option_metaboxhidden_mycpt (les deux variantes du get_user_option_{$option} filter) pour forcer les options de chargement de WordPress à partir de 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 );

et

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
gmazzap

Le problème principal:

Le principal problème ici est que dans les appels fermant, cachant- et ordre- ajax, il n'y a pas d'identifiant de publication envoyé avec la charge utile. Voici deux exemples de données de formulaire:

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

Nous pourrions résoudre ce problème en utilisant un autre appel ajax personnalisé.

Nous pourrions bien sûr simplement accrocher le crochet save_post et modifier les données chaque fois que la publication est enregistrée. Mais ce n'est pas l'expérience normale de l'interface utilisateur, donc ce n'est pas à considérer ici

Il existe une autre solution non élégante disponible avec PHP, décrite ci-dessous:

Une solution non Javascript:

La question est de savoir où stocker les données? Comme utilisateur méta-données, post méta-données ou peut-être dans un tableau personnalisé?

Ici, nous le stockons en tant que méta-données utilisateur et prenons comme exemple les boîtes de méta fermeture de post méta.

Lorsque la méta-valeur closedpostboxes_post est mise à jour, nous l'enregistrons également dans la méta-valeur closedpostboxes_post_{post_id}.

Ensuite, nous détournons l'extraction de closedpostboxes_post pour le remplacer par la méta-valeur correspondante en fonction de l'ID utilisateur et de l'ID de publication.

a) Mise à jour pendant l'action closed-postboxes ajax:

Nous pouvons récupérer l'ID de l'article, via la wp_get_referer(), puis utiliser la fonction pratique url_to_postid(). Je connaissais cette fonction "amusante" pour la première fois après avoir lu la réponse de @s_ha_dum , il y a quelques mois ;-) Malheureusement, la fonction ne reconnaît pas les variables ?post=123 GET, mais nous pouvons faire un petit truc simplement en le changeant en p=123 pour le contourner.

Nous pouvons nous connecter à updated_user_meta, qui est déclenché juste après la mise à jour des métadonnées de l'utilisateur pour 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 );

b) Récupération de données:

Nous pouvons nous connecter au hook get_user_option_closedpostboxes_post pour modifier les données extraites de la méta utilisateur 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 );

Nous voudrons peut-être aussi penser au cas où il n'y a pas de closedpostboxes_post_{post_id} basé sur la publication disponible. Donc, il utilisera les derniers paramètres enregistrés à partir de closedpostboxes_post. Peut-être voudriez-vous que tout soit ouvert ou tout fermé, dans ce cas par défaut. Il serait facile de modifier ce comportement.

Pour les autres types de publication personnalisés, nous pouvons utiliser le hook closedpostboxes_{post_type} correspondant.

Il devrait en être de même pour les ordre et masquant des métaboxes avec les méta metaboxhidden_{post_type} et meta-box-order_{post_data}.

_ {ps: désolé pour cette réponse trop longue du week-end, car ils devraient toujours être courts et enjoués ;-)

8
birgire