Ngăn bài đăng được xuất bản nếu các trường tùy chỉnh không được điền


17

Tôi có một loại bài đăng tùy chỉnh Eventcó chứa các trường tùy chỉnh bắt đầu và kết thúc ngày / lần (như metaboxes trong màn hình chỉnh sửa bài đăng).

Tôi muốn đảm bảo rằng một Sự kiện không thể được công bố (hoặc lên lịch) mà không được điền ngày, vì điều đó sẽ gây ra vấn đề với các mẫu hiển thị dữ liệu Sự kiện (bên cạnh thực tế đó là một yêu cầu cần thiết!). Tuy nhiên, tôi muốn có thể có các sự kiện Dự thảo không có ngày hợp lệ trong khi chúng đang được chuẩn bị.

Tôi đã nghĩ đến việc móc nối save_postđể kiểm tra, nhưng làm thế nào tôi có thể ngăn chặn sự thay đổi trạng thái xảy ra?

EDIT1: Đây là hook tôi đang sử dụng để lưu 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: và đây là những gì tôi đang cố sử dụng để kiểm tra dữ liệu bài đăng sau khi lưu vào cơ sở dữ liệu.

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

Vấn đề chính với điều này là một vấn đề thực sự được mô tả trong một câu hỏi khác : sử dụng wp_update_post()bên trong một save_postcái móc sẽ kích hoạt một vòng lặp vô hạn.

EDIT3: Tôi đã tìm ra một cách để làm điều đó, bằng cách móc wp_insert_post_datathay vì save_post. Vấn đề duy nhất là bây giờ post_statusđược hoàn nguyên, nhưng bây giờ một thông báo sai lệch có nội dung "Đăng bài đã xuất bản" hiển thị (bằng cách thêm &message=6vào URL được chuyển hướng), nhưng trạng thái được đặt thành Dự thảo.

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

Câu trả lời:


16

Như m0r7if3r đã chỉ ra, không có cách nào ngăn bài đăng được xuất bản bằng save_posthook, vì vào thời điểm hook được bắn, bài đã được lưu. Tuy nhiên, sau đây sẽ cho phép bạn hoàn nguyên trạng thái mà không cần sử dụng wp_insert_post_datavà không gây ra vòng lặp vô hạn.

Sau đây không được thử nghiệm, nhưng nên hoạt động.

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

Tôi đã không kiểm tra, nhưng nhìn vào mã, thông báo phản hồi sẽ hiển thị thông báo không chính xác rằng bài đăng đã được xuất bản. Điều này là do WordPress chuyển hướng chúng tôi đến một url nơi messagebiến hiện không chính xác.

Để thay đổi nó, chúng ta có thể sử dụng redirect_post_locationbộ lọc:

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

Để tóm tắt bộ lọc chuyển hướng ở trên: Nếu một bài đăng được thiết lập để được xuất bản, nhưng vẫn là một bản nháp thì chúng tôi thay đổi thông báo cho phù hợp (đó là message=10). Một lần nữa, điều này chưa được kiểm tra, nhưng nên hoạt động. Codex của add_query_arggợi ý rằng khi một biến đã được đặt, hàm sẽ thay thế nó (nhưng như tôi nói, tôi đã không kiểm tra điều này).


Khác với người mất tích; trên dòng add_query_arg của bạn, thủ thuật bộ lọc redirect_post_location này chính xác là những gì tôi cần. cảm ơn!
MadtownLems

@MadtownLems đã sửa :)
Stephen Harris

9

OK, cuối cùng đây là cách tôi kết thúc việc đó: một cuộc gọi Ajax đến một hàm PHP thực hiện kiểm tra, lấy cảm hứng từ câu trả lời này và sử dụng một mẹo thông minh từ một câu hỏi tôi đã hỏi trên StackOverflow . Điều quan trọng, tôi đảm bảo rằng chỉ khi chúng tôi muốn Xuất bản kiểm tra được thực hiện, để Bản nháp luôn có thể được lưu mà không cần kiểm tra. Điều này cuối cùng đã trở thành giải pháp dễ dàng hơn để thực sự ngăn chặn việc xuất bản bài đăng. Nó có thể giúp đỡ người khác, vì vậy tôi đã viết nó lên đây.

Đầu tiên, thêm Javascript cần thiết:

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

Sau đó, chức năng xử lý việc kiểm tra:

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

Hàm này trả về truenếu mọi thứ đều ổn và gửi biểu mẫu để xuất bản bài đăng theo kênh thông thường. Mặt khác, hàm trả về một thông báo lỗi được hiển thị dưới dạng alert()và biểu mẫu không được gửi.


Tôi đã làm theo cách tiếp cận tương tự và nhận được bài đăng được lưu dưới dạng "Bản nháp" thay vì "Xuất bản" khi chức năng xác thực trả về đúng. Không chắc chắn cách khắc phục điều đó !!! <br/> Cũng không nhận được dữ liệu cho trường textarea (ví dụ post_content, bất kỳ trường tùy chỉnh vùng văn bản nào khác) trong cuộc gọi ajax?
Mahmudur

1
Tôi đã áp dụng giải pháp này một chút khác biệt: trước hết tôi đã sử dụng đoạn mã dưới đây trong javascript trong trường hợp thành công: delayed_autosave(); //get data from textarea/tinymce field jQuery('#publish').data("valid", true).trigger('click'); //publish postRất cám ơn.
Mahmudur

3

Tôi nghĩ rằng cách tốt nhất để giải quyết vấn đề này không phải là ngăn chặn sự thay đổi trạng thái xảy ra quá nhiều như nó là để TUYỆT VỜI nếu nó xảy ra. Ví dụ: Bạn hook save_post, với mức độ ưu tiên thực sự cao (để hook sẽ bắn rất muộn, cụ thể là sau khi bạn thực hiện chèn meta), sau đó kiểm tra post_statusbài đăng vừa được lưu và cập nhật nó vào trạng thái chờ xử lý (hoặc dự thảo hoặc bất cứ điều gì) nếu nó không đáp ứng tiêu chí của bạn.

Một chiến lược thay thế sẽ là móc nối wp_insert_post_datađể đặt post_status trực tiếp. Nhược điểm của phương pháp này, theo như tôi quan tâm, là bạn sẽ chưa chèn postmeta vào cơ sở dữ liệu, vì vậy bạn sẽ phải xử lý nó, v.v. để kiểm tra, sau đó xử lý lại để chèn nó vào cơ sở dữ liệu ... có thể trở thành rất nhiều chi phí, cả về hiệu năng hoặc mã.


Tôi hiện đang kết nối save_postvới mức độ ưu tiên 1 để lưu các trường meta từ metaboxes; những gì bạn đang đề xuất sau đó là có một cái móc thứ hai save_postvới mức độ ưu tiên, giả sử, 99? Điều này sẽ đảm bảo tính toàn vẹn? Điều gì sẽ xảy ra nếu vì lý do nào đó, hook đầu tiên kích hoạt, siêu dữ liệu được chèn và bài đăng được xuất bản, nhưng hook thứ hai thì không, vì vậy bạn kết thúc với các trường không hợp lệ?
englebip

Tôi không thể nghĩ đến một tình huống trong đó cái móc đầu tiên sẽ bắn nhưng không phải là thứ hai ... bạn nghĩ loại kịch bản nào có thể gây ra điều đó? Nếu bạn lo lắng về điều đó, bạn có thể chèn meta bài đăng, kiểm tra meta bài đăng, sau đó cập nhật post_statustất cả trong một chức năng chạy khỏi một cuộc gọi thành một hook nếu bạn thích.
mor7ifer

Tôi đã đăng mã của mình dưới dạng chỉnh sửa cho câu hỏi của mình; Tôi đã cố gắng sử dụng một hook thứ hai để save_postnhưng điều đó kích hoạt một vòng lặp vô hạn.
englebip

Vấn đề của bạn là bạn nên kiểm tra bài đã tạo. Vì vậy, if( get_post_status( $post_id ) == 'publish' )những gì bạn muốn được sử dụng, vì bạn sẽ xác định lại dữ liệu trong $wpdb->posts, không phải dữ liệu trong $_POST[].
mor7ifer

0

Phương pháp tốt nhất có thể là 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>

-1

Xin lỗi tôi không thể cung cấp cho bạn một câu trả lời thẳng lên nhưng tôi nhớ lại làm một cái gì đó tương tự gần đây tôi chỉ không thể nhớ chính xác làm thế nào. Tôi nghĩ rằng tôi có thể làm điều đó theo cách này - một cái gì đó giống như tôi đã có nó là một giá trị mặc định và nếu người đó đã thay đổi nó, tôi đã chọn nó trong một câu lệnh if -> if(category==default category) {echo "You didn't pick a category!"; return them to the post creation page; }xin lỗi đây không phải là một câu trả lời thẳng thắn mà chỉ hy vọng nó giúp một chút

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.