Cegah posting agar tidak dipublikasikan jika bidang khusus tidak diisi

17

Saya memiliki jenis posting khusus Eventyang berisi bidang tanggal / waktu kustom awal dan akhir (sebagai metabox di layar edit posting).

Saya ingin memastikan bahwa suatu Acara tidak dapat dipublikasikan (atau dijadwalkan) tanpa tanggal yang diisi, karena hal itu akan menyebabkan masalah dengan templat yang menampilkan data Acara (selain fakta bahwa itu merupakan persyaratan yang diperlukan!). Namun, saya ingin dapat memiliki acara Draft yang tidak mengandung tanggal yang valid saat sedang dalam persiapan.

Saya berpikir save_postuntuk melakukan pengecekan, tetapi bagaimana saya bisa mencegah perubahan status terjadi?

EDIT1: Ini adalah kait yang saya gunakan sekarang untuk menyimpan 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: dan inilah yang saya coba gunakan untuk memeriksa data postingan setelah menyimpan ke database.

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

Masalah utama dengan ini adalah masalah yang sebenarnya dijelaskan dalam pertanyaan lain : menggunakan di wp_update_post()dalam save_postkail memicu loop tak terbatas.

EDIT3: Saya menemukan cara untuk melakukannya, dengan mengait wp_insert_post_databukannya save_post. Satu-satunya masalah adalah bahwa sekarang post_statussudah dikembalikan, tetapi sekarang pesan yang menyesatkan mengatakan "Posting diterbitkan" muncul (dengan menambahkan &message=6ke URL yang dialihkan), tetapi statusnya diatur ke Konsep.

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;
}
englebip
sumber

Jawaban:

16

Seperti yang ditunjukkan oleh m0r7if3r, tidak ada cara untuk mencegah posting dipublikasikan menggunakan save_posthook, karena pada saat hook dipecat, post tersebut sudah disimpan. Namun, yang berikut ini akan memungkinkan Anda untuk mengembalikan status tanpa menggunakan wp_insert_post_datadan tanpa menyebabkan perulangan tak terbatas.

Berikut ini tidak diuji, tetapi harus berfungsi.

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

Saya belum memeriksa, tetapi melihat kode, pesan umpan balik akan menampilkan pesan yang salah bahwa kiriman diterbitkan. Ini karena WordPress mengarahkan kita ke url di mana messagevariabelnya sekarang salah.

Untuk mengubahnya, kita bisa menggunakan redirect_post_locationfilter:

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

Untuk meringkas filter redirect di atas: Jika posting diatur untuk diterbitkan, tetapi masih konsep maka kami mengubah pesan sesuai (yang message=10). Sekali lagi, ini tidak diuji, tetapi harus berhasil. Codex dariadd_query_arg menyarankan bahwa ketika suatu variabel sudah diatur, fungsi menggantinya (tetapi seperti yang saya katakan, saya belum menguji ini).

Stephen Harris
sumber
Selain yang hilang; pada baris add_query_arg Anda, trik filter redirect_post_location ini persis seperti yang saya butuhkan. Terima kasih!
MadtownLems
@MadtownLems diperbaiki :)
Stephen Harris
9

OK, ini akhirnya bagaimana saya akhirnya melakukannya: panggilan Ajax ke fungsi PHP yang melakukan pengecekan, semacam terinspirasi oleh jawaban ini dan menggunakan tip cerdas dari pertanyaan yang saya ajukan di StackOverflow . Yang penting, saya memastikan bahwa hanya ketika kami ingin mempublikasikan pengecekan selesai, sehingga Draft selalu dapat disimpan tanpa pengecekan. Ini akhirnya menjadi solusi yang lebih mudah untuk benar-benar dicegah publikasi posting. Mungkin membantu orang lain, jadi saya menulisnya di sini.

Pertama, tambahkan Javascript yang diperlukan:

//AJAX to validate event before publishing
//adapted from /wordpress/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
}
}

Kemudian, fungsi yang menangani pemeriksaan:

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 /wordpress//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();
}

Fungsi ini kembali truejika semuanya baik-baik saja, dan mengirimkan formulir untuk mempublikasikan posting oleh saluran normal. Jika tidak, fungsi mengembalikan pesan kesalahan yang ditampilkan sebagai alert(), dan formulir tidak dikirimkan.

englebip
sumber
Saya telah mengikuti pendekatan yang sama dan membuat pos disimpan sebagai "Konsep" alih-alih "Terbitkan" ketika fungsi validasi mengembalikan true. Tidak yakin bagaimana cara memperbaikinya !!! <br/> Juga tidak mendapatkan data untuk bidang textarea (mis. Post_content, bidang kustom area teks lainnya) selama panggilan ajax?
Mahmudur
1
Saya telah menerapkan solusi ini sedikit berbeda: pertama-tama saya telah menggunakan kode di bawah ini dalam javascript jika sukses: delayed_autosave(); //get data from textarea/tinymce field jQuery('#publish').data("valid", true).trigger('click'); //publish postBanyak terima kasih.
Mahmudur
3

Saya pikir cara terbaik untuk menyelesaikannya adalah bukan MENCEGAH perubahan status agar tidak terjadi sebanyak REVERT jika itu terjadi. Misalnya: Anda mengait save_post, dengan prioritas sangat tinggi (sehingga kait akan menyala sangat terlambat, yaitu setelah Anda melakukan meta insert Anda), lalu periksa post_statuspos yang baru saja disimpan, dan perbarui ke tertunda (atau konsep atau terserah) jika tidak memenuhi kriteria Anda.

Strategi alternatif adalah mengaitkan wp_insert_post_datauntuk mengatur post_status secara langsung. Kerugian dari metode ini, sejauh yang saya ketahui, adalah bahwa Anda tidak akan memasukkan postmeta ke dalam database, jadi Anda harus memprosesnya, dll di tempat untuk melakukan pemeriksaan Anda, kemudian memprosesnya lagi untuk memasukkan itu ke dalam basis data ... yang bisa menjadi banyak overhead, baik dalam kinerja atau dalam kode.

mor7ifer
sumber
Saat ini saya sedang mengaitkan save_postdengan prioritas 1 untuk menyimpan bidang meta dari metabox; apa yang Anda usulkan kemudian adalah memiliki kaitan kedua save_postdengan prioritas, katakanlah, 99? Apakah ini menjamin integritas? Bagaimana jika karena suatu alasan kait pertama menyala, metadata dimasukkan dan pos diterbitkan, tetapi kait kedua tidak, sehingga Anda berakhir dengan bidang yang tidak valid?
englebip
Saya tidak bisa memikirkan situasi di mana kait pertama akan menembak tetapi bukan yang kedua ... skenario seperti apa yang Anda pikirkan yang dapat menyebabkan itu? Jika Anda sangat khawatir tentang hal itu, Anda dapat memasukkan meta pos, memeriksa meta pos, dan kemudian memperbarui post_statussemua dalam satu fungsi yang menjalankan satu panggilan ke kail jika Anda mau.
mor7ifer
Saya memposting kode saya sebagai edit untuk pertanyaan saya; Saya mencoba menggunakan kait kedua untuk save_posttetapi itu memicu loop tak terbatas.
englebip
Masalah Anda adalah Anda harus memeriksa pos yang dibuat. Jadi if( get_post_status( $post_id ) == 'publish' )apa yang ingin Anda gunakan, karena Anda akan mendefinisikan ulang data $wpdb->posts, bukan data masuk $_POST[].
mor7ifer
0

Metode terbaik mungkin 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>
T.Todua
sumber
-1

Maaf saya tidak bisa memberikan jawaban langsung tetapi saya ingat melakukan sesuatu yang serupa baru-baru ini saya hanya tidak ingat persis bagaimana caranya. Saya pikir saya mungkin melakukannya tentang cara - sesuatu seperti saya telah menjadi nilai default dan jika orang itu tidak mengubahnya saya mengambil ini dalam pernyataan if jadi -> if(category==default category) {echo "You didn't pick a category!"; return them to the post creation page; }maaf ini bukan jawaban langsung tetapi harapan ini sedikit membantu.

MIINIIM
sumber