¿Cómo hacer que los metaboxes abiertos / cerrados y ocultos / mostrados se guarden por publicación?

8

Mi problema real es un poco complejo, así que intentaré abstraerlo y hacerlo simple.

Estoy trabajando en una aplicación personalizada basada en WordPress. Registré un tipo de publicación personalizada, llamémosle "personas" donde almaceno información sobre ... personas.

El CPT solo admite campos de título de publicación y contenido predeterminado de la publicación, pero hay algunos metaboxes para almacenar información personal (piense en mi aplicación como una libreta de direcciones).

Entonces hay un metabox para almacenar información personal, uno para almacenar información de redes sociales, otro para almacenar información relacionada con el trabajo, es decir, si esa persona es para mí un cliente, un proveedor, si tenemos créditos o débitos ...

Simplifiqué aquí, pero hay una cantidad constante de metaboxes, digamos 12.

Mi problema es que algunas personas para las que quiero almacenar información son solo contactos aleatorios, y quiero almacenar solo información personal, otras son amigos y quiero almacenar información personal e información de redes sociales, otras son clientes o Los proveedores y yo queremos almacenar información relacionada con el trabajo.

Si al editar una publicación oculto (a través de menú de opciones de pantalla ) o cierro cualquier metabox que no haga. Necesito, cuando abro otro post donde los necesito, tengo que mostrarlos o abrirlos de nuevo. Eso es porque los metaboxes position / status / order se guardan por usuario como metadatos del usuario .

Si imaginas que en algunas publicaciones necesito 2 metaboxes, en unos 10 y en unos 5, entiendes que eso es molesto porque mantenerlos todos mostrados / abiertos hace que la pantalla de edición sea accesible (la barra de desplazamiento parece interminable), y algunas veces la información I buscar está al final de la página después de un grupo de metaboxes sin información ...

Pregunta :

¿Es posible guardar metaboxes posición / estado / orden por publicación para un tipo de publicación específica?

PD: Sé que algunos js / jQuery pueden resolver el problema, pero si es posible evitaría las soluciones de JavaScript.

    
pregunta gmazzap 13.09.2014 - 17:41

2 respuestas

6

Como señala birgire en su respuesta , WordPress usa AJAX para actualizar el estado de metaboxes y los datos pasados en la solicitud de AJAX no incluyen la identificación de la publicación, y eso dificulta la actualización del estado de los cuadros por publicación.

Una vez que encontré que la acción AJAX utilizada por WordPress es 'closed-postboxes' , busqué esta cadena en la carpeta admin js para ver cómo WordPress realiza la solicitud AJAX.

Encontré que sucede en postbox.js en la línea # 118 .

Parece que sí:

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

Esencialmente, WordPress mira los elementos de DOM con la clase 'postbox' y la clase 'closed' y crea una lista separada por comas de sus ID. Lo mismo se hace para los elementos ocultos de DOM con la clase 'buzón'.

Por lo tanto, mi idea fue: puedo crear un metabox falso que tenga las clases correctas y que esté oculta, configurando su id para que contenga ID de publicación, y de esta manera puedo recuperarla en un Solicitud de AJAX.

Esto es lo que he hecho:

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 esta manera, creé un metabox que siempre está cerrado y siempre oculto, por lo que WordPress enviará su ID como $_POST var en la solicitud AJAX, y una vez que la identificación de la caja falsa contenga la ID de la publicación de una manera predecible, estoy capaz de reconocer la publicación.

Después de eso, observé cómo WordPress realiza la tarea AJAX.

En admin-ajax.php en la línea 72 , WordPress engancha 'wp_ajax_closed-postboxes' con prioridad 1.

Entonces, para actuar antes de WordPress, podría conectar la misma acción con prioridad 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 );

Tener los datos guardados en una publicación meta hizo posible filtrar get_user_option_closedpostboxes_mycpt y get_user_option_metaboxhidden_mycpt (ambas variaciones de la get_user_option_{$option} filtro) para forzar las opciones de carga de WordPress desde meta 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 );

y

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 );
    
respondido por el gmazzap 26.10.2014 - 10:54
7

El problema principal:

El principal problema aquí es que en las llamadas ajax closing- , hiding- y ordering- , no se envía ninguna ID de publicación con el carga útil. Aquí hay dos ejemplos de datos de formulario:

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

Podríamos solucionar esto utilizando otra llamada ajax personalizada.

Por supuesto, podríamos simplemente enganchar en el enganche save_post y modificar los datos cada vez que se guarde la publicación. Pero esa no es la experiencia normal de UI, por lo que no se considera aquí

Hay otra solución no elegante disponible con PHP, que se describe a continuación:

Una solución no Javascript:

La pregunta es ¿dónde almacenar los datos? Como usuario metadatos, publique metadatos o tal vez en una tabla personalizada?

Aquí lo almacenamos como metadatos del usuario y tomamos como ejemplo el cierre de publicación .

Cuando se actualiza el valor meta closedpostboxes_post , también lo guardamos en el valor meta closedpostboxes_post_{post_id} .

Luego secuestramos la recuperación de closedpostboxes_post para anularlo con el valor meta correspondiente basado en la identificación del usuario y la identificación de la publicación.

a) Actualización durante la acción closed-postboxes ajax:

Podemos obtener la ID de la publicación, a través de wp_get_referer() y luego usar la práctica función url_to_postid() . La primera vez que supe de esta función "divertida" después de leer la respuesta de @s_ha_dum , hace unos meses ;-) Lamentablemente La función no reconoce las variables ?post=123 GET, pero podemos hacer un pequeño truco simplemente cambiándolo a p=123 para evitarlo.

Podemos enlazar con updated_user_meta , que se activa justo después de que se hayan actualizado los metadatos del usuario para 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) Obteniendo datos:

Podemos enganchar el enganche get_user_option_closedpostboxes_post para modificar los datos obtenidos de la meta closedpostboxes_post user:

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

También podríamos pensar en el caso en el que no hay un closedpostboxes_post_{post_id} basado en publicaciones disponible. Por lo tanto, utilizará la última configuración guardada de closedpostboxes_post . Quizás desee tener todo abierto o cerrado, en ese caso predeterminado. Sería fácil modificar este comportamiento.

Para otros tipos de publicaciones personalizadas, podemos usar el correspondiente closedpostboxes_{post_type} hook.

Lo mismo debería ser posible para ordenar y ocultar de metaboxes con el meta de usuario metaboxhidden_{post_type} y meta-box-order_{post_data} .

ps: disculpa la respuesta de este fin de semana demasiado largo, ya que siempre deben ser cortos & alegre ;-)

    
respondido por el birgire 13.09.2014 - 22:39

Lea otras preguntas en las etiquetas