Impedir que la publicación se publique si no se rellenan los campos personalizados

14

Tengo un tipo de publicación personalizado Event que contiene los campos personalizados de fecha / hora de inicio y finalización (como metaboxes en la pantalla de edición de publicación).

Me gustaría asegurarme de que un Evento no pueda publicarse (o programarse) sin que se llenen las fechas, ya que eso causará problemas con las plantillas que muestran los datos del Evento (además del hecho de que es un requisito necesario). Sin embargo, me gustaría poder tener Borradores de eventos que no contengan una fecha válida mientras están en preparación.

Estaba pensando en enganchar a save_post para hacer la verificación, pero ¿cómo puedo evitar que ocurra el cambio de estado?

EDIT1: Este es el gancho que estoy usando ahora para guardar el post_meta.

// Save the Metabox Data
function ep_eventposts_save_meta( $post_id, $post ) {

if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
    return;

if ( !isset( $_POST['ep_eventposts_nonce'] ) )
    return;

if ( !wp_verify_nonce( $_POST['ep_eventposts_nonce'], plugin_basename( __FILE__ ) ) )
    return;

// Is the user allowed to edit the post or page?
if ( !current_user_can( 'edit_post', $post->ID ) )
    return;

// OK, we're authenticated: we need to find and save the data
// We'll put it into an array to make it easier to loop though

//debug
//print_r($_POST);

$metabox_ids = array( '_start', '_end' );

foreach ($metabox_ids as $key ) {
    $events_meta[$key . '_date'] = $_POST[$key . '_date'];
    $events_meta[$key . '_time'] = $_POST[$key . '_time'];
    $events_meta[$key . '_timestamp'] = $events_meta[$key . '_date'] . ' ' . $events_meta[$key . '_time'];
}

$events_meta['_location'] = $_POST['_location'];

if (array_key_exists('_end_timestamp', $_POST))
    $events_meta['_all_day'] = $_POST['_all_day'];

// Add values of $events_meta as custom fields

foreach ( $events_meta as $key => $value ) { // Cycle through the $events_meta array!
    if ( $post->post_type == 'revision' ) return; // Don't store custom data twice
    $value = implode( ',', (array)$value ); // If $value is an array, make it a CSV (unlikely)
    if ( get_post_meta( $post->ID, $key, FALSE ) ) { // If the custom field already has a value
        update_post_meta( $post->ID, $key, $value );
    } else { // If the custom field doesn't have a value
        add_post_meta( $post->ID, $key, $value );
    }
    if ( !$value ) 
                delete_post_meta( $post->ID, $key ); // Delete if blank
}

}

add_action( 'save_post', 'ep_eventposts_save_meta', 1, 2 );

EDIT2: y esto es lo que trato de usar para verificar los datos de la publicación después de guardar en la base de datos.

add_action( 'save_post', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $post_id, $post ) {
//check that metadata is complete when a post is published
//print_r($_POST);

if ( $_POST['post_status'] == 'publish' ) {

    $custom = get_post_custom($post_id);

    //make sure both dates are filled
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $post->post_status = 'draft';
        wp_update_post($post);

    }
    //make sure start < end
    elseif ( $custom['_start_timestamp'] > $custom['_end_timestamp'] ) {
        $post->post_status = 'draft';
        wp_update_post($post);
    }
    else {
        return;
    }
}
}

El problema principal con esto es un problema que realmente se describió en otra pregunta : usar wp_update_post() dentro de un save_post hook activa un bucle infinito.

EDIT3: encontré una forma de hacerlo, conectando wp_insert_post_data en lugar de save_post . El único problema es que ahora se revierte post_status , pero ahora aparece un mensaje engañoso que dice "Publicación publicada" (al agregar &message=6 a la URL redirigida), pero el estado se establece en Borrador.

add_filter( 'wp_insert_post_data', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $data, $postarr ) {
//check that metadata is complete when a post is published, otherwise revert to draft
if ( $data['post_type'] != 'event' ) {
    return $data;
}
if ( $postarr['post_status'] == 'publish' ) {
    $custom = get_post_custom($postarr['ID']);

    //make sure both dates are filled
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $data['post_status'] = 'draft';
    }
    //make sure start < end
    elseif ( $custom['_start_timestamp'] > $custom['_end_timestamp'] ) {
        $data['post_status'] = 'draft';
    }
    //everything fine!
    else {
        return $data;
    }
}

return $data;
}
    
pregunta englebip 12.02.2012 - 00:31

5 respuestas

9

Bien, así es como terminé haciéndolo: una llamada Ajax a una función PHP que realiza la comprobación, inspirada en esta respuesta y utilizando una sugerencia inteligente de una pregunta que formulé en StackOverflow . Y lo que es más importante, me aseguro de que solo cuando queremos publicar la comprobación se realice, de modo que siempre se pueda guardar un borrador sin realizar la comprobación. Esto terminó siendo la solución más fácil para en realidad evitar la publicación de la publicación. Podría ayudar a alguien más, así que lo escribí aquí.

Primero, agregue el Javascript necesario:

//AJAX to validate event before publishing
//adapted from https://wordpress.stackexchange.com/questions/15546/dont-publish-custom-post-type-post-if-a-meta-data-field-isnt-valid
add_action('admin_enqueue_scripts-post.php', 'ep_load_jquery_js');   
add_action('admin_enqueue_scripts-post-new.php', 'ep_load_jquery_js');   
function ep_load_jquery_js(){
global $post;
if ( $post->post_type == 'event' ) {
    wp_enqueue_script('jquery');
}
}

add_action('admin_head-post.php','ep_publish_admin_hook');
add_action('admin_head-post-new.php','ep_publish_admin_hook');
function ep_publish_admin_hook(){
global $post;
if ( is_admin() && $post->post_type == 'event' ){
    ?>
    <script language="javascript" type="text/javascript">
        jQuery(document).ready(function() {
            jQuery('#publish').click(function() {
                if(jQuery(this).data("valid")) {
                    return true;
                }
                var form_data = jQuery('#post').serializeArray();
                var data = {
                    action: 'ep_pre_submit_validation',
                    security: '<?php echo wp_create_nonce( 'pre_publish_validation' ); ?>',
                    form_data: jQuery.param(form_data),
                };
                jQuery.post(ajaxurl, data, function(response) {
                    if (response.indexOf('true') > -1 || response == true) {
                        jQuery("#post").data("valid", true).submit();
                    } else {
                        alert("Error: " + response);
                        jQuery("#post").data("valid", false);

                    }
                    //hide loading icon, return Publish button to normal
                    jQuery('#ajax-loading').hide();
                    jQuery('#publish').removeClass('button-primary-disabled');
                    jQuery('#save-post').removeClass('button-disabled');
                });
                return false;
            });
        });
    </script>
    <?php
}
}

Luego, la función que maneja la comprobación:

add_action('wp_ajax_ep_pre_submit_validation', 'ep_pre_submit_validation');
function ep_pre_submit_validation() {
//simple Security check
check_ajax_referer( 'pre_publish_validation', 'security' );

//convert the string of data received to an array
//from https://wordpress.stackexchange.com/a/26536/10406
parse_str( $_POST['form_data'], $vars );

//check that are actually trying to publish a post
if ( $vars['post_status'] == 'publish' || 
    (isset( $vars['original_publish'] ) && 
     in_array( $vars['original_publish'], array('Publish', 'Schedule', 'Update') ) ) ) {
    if ( empty( $vars['_start_date'] ) || empty( $vars['_end_date'] ) ) {
        _e('Both Start and End date need to be filled');
        die();
    }
    //make sure start < end
    elseif ( $vars['_start_date'] > $vars['_end_date'] ) {
        _e('Start date cannot be after End date');
        die();
    }
    //check time is also inputted in case of a non-all-day event
    elseif ( !isset($vars['_all_day'] ) ) {
        if ( empty($vars['_start_time'] ) || empty( $vars['_end_time'] ) ) {
            _e('Both Start time and End time need to be specified if the event is not an all-day event');
            die();              
        }
        elseif ( strtotime( $vars['_start_date']. ' ' .$vars['_start_time'] ) > strtotime( $vars['_end_date']. ' ' .$vars['_end_time'] ) ) {
            _e('Start date/time cannot be after End date/time');
            die();
        }
    }
}

//everything ok, allow submission
echo 'true';
die();
}

Esta función devuelve true si todo está bien, y envía el formulario para publicar la publicación por el canal normal. De lo contrario, la función devuelve un mensaje de error que se muestra como alert() y el formulario no se envía.

    
respondido por el englebip 17.02.2012 - 18:31
13

Como m0r7if3r señaló, no hay manera de evitar que se publique una publicación utilizando el gancho save_post , ya que en el momento en que se activa el enlace, la publicación ya está guardada. Sin embargo, lo siguiente le permitirá revertir el estado sin usar wp_insert_post_data y sin causar un bucle infinito.

Lo siguiente no está probado, pero debería funcionar.

<?php
add_action('save_post', 'my_save_post');
function my_save_post($post_id) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
         return;

    if ( !isset( $_POST['ep_eventposts_nonce'] ) )
         return;

    if ( !wp_verify_nonce( $_POST['ep_eventposts_nonce'], plugin_basename( __FILE__ ) ) )
         return;

    // Is the user allowed to edit the post or page?
     if ( !current_user_can( 'edit_post', $post->ID ) )
         return;

   // Now perform checks to validate your data. 
   // Note custom fields (different from data in custom metaboxes!) 
   // will already have been saved.
    $prevent_publish= false;//Set to true if data was invalid.
    if ($prevent_publish) {
        // unhook this function to prevent indefinite loop
        remove_action('save_post', 'my_save_post');

        // update the post to change post status
        wp_update_post(array('ID' => $post_id, 'post_status' => 'draft'));

        // re-hook this function again
        add_action('save_post', 'my_save_post');
    }
}
?>

No lo he comprobado, pero al mirar el código, el mensaje de comentarios mostrará el mensaje incorrecto en el que se publicó la publicación. Esto se debe a que WordPress nos redirige a una url donde la variable message ahora es incorrecta.

Para cambiarlo, podemos usar el filtro redirect_post_location :

add_filter('redirect_post_location','my_redirect_location',10,2);
function my_redirect_location($location,$post_id){
    //If post was published...
    if (isset($_POST['publish'])){
        //obtain current post status
        $status = get_post_status( $post_id );

        //The post was 'published', but if it is still a draft, display draft message (10).
        if($status=='draft')
            $location = add_query_arg('message', 10, $location);
    }

    return $location;
}

Para resumir el filtro de redireccionamiento anterior: si una publicación está configurada para publicarse, pero sigue siendo un borrador, modificamos el mensaje en consecuencia (que es message=10 ). Nuevamente, esto no está probado, pero debería funcionar. El Códice de add_query_arg sugiere que cuando una variable ya está establecida, la función la reemplaza (pero como digo , No he probado esto).

    
respondido por el Stephen Harris 13.02.2012 - 17:04
3

Creo que la mejor manera de hacerlo es no EVITAR que el cambio de estado ocurra tanto como REVERTIRLO si lo hace. Por ejemplo: Engancha save_post , con una prioridad realmente alta (de modo que el enganche se activará muy tarde, es decir, después de que hayas metido tu meta), luego verifica el post_status de la publicación que se acaba de guardar y actualiza. pendiente (o borrador o lo que sea) si no cumple con sus criterios.

Una estrategia alternativa sería enganchar wp_insert_post_data para establecer el post_status directamente. La desventaja de este método, en lo que a mí respecta, es que aún no habrá insertado el postmeta en la base de datos, por lo que tendrá que procesarlo, etc. para hacer sus cheques y luego procesarlo nuevamente para insertar en la base de datos ... lo que podría convertirse en una gran sobrecarga, ya sea en rendimiento o en código.

    
respondido por el mor7ifer 12.02.2012 - 00:53
0

El mejor método puede ser JAVASCRIPT:

<script type="text/javascript">
var field_id =  "My_field_div__ID";    // <----------------- CHANGE THIS

var SubmitButton = document.getElementById("save-post") || false;
var PublishButton = document.getElementById("publish")  || false; 
if (SubmitButton)   {SubmitButton.addEventListener("click", SubmCLICKED, false);}
if (PublishButton)  {PublishButton.addEventListener("click", SubmCLICKED, false);}
function SubmCLICKED(e){   
  var passed= false;
  if(!document.getElementById(field_id)) { alert("I cant find that field ID !!"); }
  else {
      var Enabled_Disabled= document.getElementById(field_id).value;
      if (Enabled_Disabled == "" ) { alert("Field is Empty");   }  else{passed=true;}
  }
  if (!passed) { e.preventDefault();  return false;  }
}
</script>
    
respondido por el T.Todua 26.06.2015 - 12:56
-1

Lo siento, no puedo darte una respuesta directa, pero sí recuerdo haber hecho algo similar hace poco, no puedo recordar exactamente cómo. Creo que tal vez lo hice de forma aproximada, algo así como que tenía un valor predeterminado y, si la persona no lo había cambiado, lo detecté en una declaración if - > if(category==default category) {echo "You didn't pick a category!"; return them to the post creation page; } lo siento, esta no es una respuesta directa, pero espero que ayude un poco.

    
respondido por el MIINIIM 12.02.2012 - 02:20

Lea otras preguntas en las etiquetas