Comment faire pour enregistrer l'état des métaboxes ouvertes / fermées et masquées / affichées par poste?

9

Mon vrai problème est un peu complexe, je vais donc essayer 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 publication personnalisé, appelons-le "personnes" où je stocke des informations sur ... les personnes.

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

Il y a donc une métabox pour stocker des informations personnelles, une pour stocker des informations sur les réseaux sociaux, une autre pour stocker des informations liées 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é cohérente 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 je veux stocker uniquement des informations personnelles, d'autres sont des amis et je veux stocker des informations personnelles et des informations sur les réseaux sociaux, d'autres sont des clients ou des fournisseurs et je souhaitez stocker des informations liées au travail.

Si, lors de la modification d'un article, je me cache (via le menu d'options de l'écran ) ou ferme une métabox dont je n'ai pas besoin, lorsque j'ouvre un autre article là où j'en ai besoin, je dois les afficher ou les rouvrir. Cela parce que la position / le statut / l'ordre des métaboxes sont enregistrés par utilisateur en tant que métadonnées utilisateur .

Si vous imaginez dans certains articles que j'ai besoin de 2 métaboxes, dans certains 10 et dans 5, vous comprenez que c'est ennuyeux parce que les garder tous affichés / ouverts rendent l'écran d'édition bas accessible (la barre de défilement semble infinie), et parfois les informations que je recherche sont à la fin de la page après un tas de métaboxes sans info ...

Question:

Est-il possible d'enregistrer la position / le statut / l'ordre des métaboxes par poste pour un type de poste spécifique?


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

gmazzap
la source

Réponses:

8

Le problème principal:

Le principal problème ici est que dans les appels de fermeture , de masquage et de commande ajax, aucun ID de poste n'est 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 contourner cela en utilisant un autre appel ajax personnalisé.

Nous pourrions bien sûr simplement accrocher le save_postcrochet et modifier les données chaque fois que le message est enregistré. Mais ce n'est pas l'expérience normale de l'interface utilisateur, donc ce n'est pas considéré 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? En tant que métadonnées utilisateur , publier des métadonnées ou peut-être dans un tableau personnalisé?

Ici, nous les stockons en tant que métadonnées utilisateur et prenons l'exemple de la fermeture des boîtes post méta.

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

Ensuite, nous détournons la récupération de closedpostboxes_postpour la remplacer par la valeur méta correspondante basée sur l'ID utilisateur et l'ID de publication.

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

Nous pouvons récupérer l'ID du message, via le wp_get_referer()puis utiliser la url_to_postid()fonction pratique . J'ai connu cette fonction "drôle" 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 ?post=123variables GET, mais nous pouvons faire un petit tour en la changeant simplement p=123pour la contourner.

Nous pouvons nous connecter updated_user_meta, qui est déclenché juste après la mise à jour des métadonnées 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 des données:

Nous pouvons accrocher le get_user_option_closedpostboxes_postcrochet pour modifier les données extraites de la closedpostboxes_postméta utilisateur:

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 pourrions aussi vouloir penser au cas où il n'y a pas de poste basé closedpostboxes_post_{post_id}disponible. Il utilisera donc les derniers paramètres enregistrés à partir de closedpostboxes_post. Vous voudrez peut-être que tout soit ouvert ou fermé, dans ce cas par défaut. Il serait facile de modifier ce comportement.

Pour d'autres types de poteaux personnalisés, nous pouvons utiliser le closedpostboxes_{post_type}crochet correspondant .

Il devrait en être de même pour la commande et le masquage des métaboxes avec les méta metaboxhidden_{post_type}et meta-box-order_{post_data}user.

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

Birgire
la source
Excellent +1. N / P pour une réponse longue, je ne m'attendrais pas à des réponses courtes. Pour être honnête, je ne m'y attendais pas le week-end :) Deux choses que j'ai beaucoup aimées: d'abord l'idée de stocker des données par utilisateur et par poste: mon idée était de stocker dans le post meta, mais de cette façon tout les utilisateurs auront le même statut. 2e l'idée à utiliser 'get_user_option_*_post'pour que WP reconnaisse les données personnalisées. Je pense seulement que je n'aime pas trop, c'est l'utilisation de wp_get_referercela vraiment sur $_SERVERvar qui n'est pas vraiment fiable mais je pense que j'ai une idée pour surmonter le "problème principal";)
gmazzap
Merci, je suppose que cela dépend du nombre d'utilisateurs et de publications où il serait préférable de stocker les données. Peut-être que ces données devraient avoir un certain TTL et être effacées, par exemple une fois par mois? Oui, je suis d'accord avec vous concernant la wp_get_referer()méthode, c'est pourquoi je l'ai appelée une solution PHP non élégante ;-) J'ai d'abord pensé à stocker l'identifiant de publication actuel pour chaque utilisateur, mais cela ne fonctionne pas si un utilisateur en édite deux ou plus messages dans le navigateur. Réjouissez-vous d'entendre votre idée sur le "problème principal" Profitez du week-end ;-)
birgire
Après 43 jours, un vote positif m'a rappelé de répondre à cette question. Merci encore pour votre réponse.
gmazzap
6

Comme l'a souligné birgire dans sa réponse , WordPress utilise AJAX pour mettre à jour l'état des métaboxes et les données transmises dans la demande AJAX n'incluent pas l'identifiant de publication, ce qui rend difficile la mise à jour de l'état des boîtes par publication.

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

J'ai trouvé que cela se produit postbox.jsà la ligne # 118 .

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

Essentiellement, WordPress examine les éléments DOM avec la classe `` boîte aux lettres '' et la classe `` fermée '' et crée une liste séparée par des virgules de leurs ID. La même chose est faite pour les éléments DOM cachés avec la classe «boîte aux lettres».

Donc, ma pensée était: je peux créer une fausse métabox qui a les bonnes classes et qui est cachée, en définissant son id pour contenir l'ID du post, et de cette façon je peux le récupérer dans une requête AJAX.

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 métabox qui est toujours fermée et toujours cachée, donc WordPress enverra son ID en tant que $_POSTvar dans la demande AJAX, et une fois que l'id de la fausse boîte contient l'ID de la poste de manière prévisible, je suis capable de reconnaître la publication.

Après cela, j'ai regardé comment WordPress exécute la tâche AJAX.

À la admin-ajax.phpligne 72 , WordPress s'accroche 'wp_ajax_closed-postboxes'avec la priorité 1.

Donc, pour agir avant WordPress, je pouvais 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 );

La sauvegarde des données dans un post meta a permis de filtrer get_user_option_closedpostboxes_mycptet get_user_option_metaboxhidden_mycpt(les deux variantes du get_user_option_{$option}filtre) de forcer les options de chargement WordPress à partir du 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 );
gmazzap
la source
Quelle bonne idée d'utiliser une métabox cachée avec les informations pertinentes +1
birgire
merci @birgire et merci encore pour votre A, l'idée de sauvegarder les données à la fois par utilisateur et par poste est à vous :)
gmazzap