Запобігати публікації публікації, якщо спеціальні поля не заповнені


17

У мене є власний тип публікації Event який містить початкові та кінцеві користувацькі поля дати / кінця (як метабокси на екрані редагування публікацій).

Я хотів би переконатися, що подія не може бути опублікована (або запланована) без заповнення дат, оскільки це спричинить проблеми з шаблонами, що відображають дані події (крім того, що це необхідна вимога!). Однак я хотів би мати можливість проекту проекту подій, які не містять дійсної дати, поки вони готуються.

Я думав підключитись, save_postщоб зробити перевірку, але як я можу не допустити зміни статусу?

EDIT1: Це гачок, який я зараз використовую, щоб зберегти 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: і це те, що я намагаюся використовувати для перевірки даних публікації після збереження в базі даних.

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

Основна проблема цього - проблема, яка була фактично описана в іншому питанні : використання wp_update_post()всередині save_postгака запускає нескінченну петлю.

EDIT3: Я зрозумів , спосіб зробити це, підключивши wp_insert_post_dataзамість save_post. Єдина проблема полягає в тому, що тепер post_statusце повернене, але тепер оманливе повідомлення із надписом "Опубліковано" з'являється (додавши &message=6до перенаправленої URL-адреси), але статус встановлено у Чернетку.

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

Відповіді:


16

Як вказував m0r7if3r, немає жодного способу запобігти публікації публікації за допомогою save_postгака, оскільки до моменту, коли цей гачок випущений, публікація вже збережена. Наступне, однак, дозволить повернути статус без використання wp_insert_post_dataта без заподіяння нескінченного циклу.

Далі не перевіряється, але має працювати.

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

Я не перевіряв, але, дивлячись на код, у повідомленні зворотного зв’язку відображатиметься неправильне повідомлення про те, що публікація була опублікована. Це тому, що WordPress перенаправляє нас до URL-адреси, де messageзмінна зараз неправильна.

Щоб змінити його, ми можемо використовувати 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;
}

Підсумовуючи наведений вище фільтр переспрямування: Якщо публікація встановлена ​​для публікації, але все ще є чернетка, то ми змінюємо повідомлення відповідно (що є message=10). Знову ж таки, це неперевірено, але має працювати. Кодекс add_query_argпропонує, що коли змінну вже встановлено, функція її замінює (але, як я вже говорив, я цього не перевіряв).


Окрім зниклих; у вашому рядку add_query_arg, цей фільтр з перенаправлення_пост_локації - саме те, що мені потрібно. Спасибі!
MadtownLems

@MadtownLems fix :)
Стівен Харріс

9

Гаразд, ось, нарешті, я це зробив: виклик Ajax до функції PHP, який робить перевірку, начебто натхненний цією відповіддю та використовуючи розумну підказку з питання, яке я задав на StackOverflow . Що важливо, я переконуюсь, що лише тоді, коли ми хочемо опублікувати перевірку, буде зроблено так, щоб чернетку завжди можна було зберегти без перевірки. Це в кінцевому підсумку стало простішим рішенням для фактичного запобігання публікації публікації. Це може допомогти комусь іншому, тому я написав це тут.

Спочатку додайте необхідний Javascript:

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

Потім функція, яка обробляє перевірку:

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

Ця функція повертається, trueякщо все добре, і надсилає форму для публікації публікації звичайним каналом. В іншому випадку функція повертає повідомлення про помилку, яке відображається як alert(), а форма не подається.


Я дотримувався того ж підходу і отримував збереження публікації як "Чернетка" замість "Опублікувати", коли функція перевірки повернеться істиною. Не знаєте, як це виправити !!! <br/> Також не отримувати дані для текстового поля (наприклад, post_content, будь-яке інше користувальницьке поле тексту) під час виклику Ajax?
Махмудур

1
Я застосував це рішення дещо інакше: перш за все, я застосував наведений нижче код у JavaScript у випадку успіху: delayed_autosave(); //get data from textarea/tinymce field jQuery('#publish').data("valid", true).trigger('click'); //publish postВелике спасибі.
Махмудур

3

Я думаю, що найкращий спосіб досягти цього - це не ПЕРЕБУТИТИ зміну статусу, щоб відбутися так сильно, як це ПОВЕРНУТИСЯ, якщо це станеться. Наприклад: Ви зачіпаєте save_postдійсно високий пріоритет (щоб гак запустився дуже пізно, а саме після того, як ви зробите мета-вставлення), потім перевірте post_statusщойно збережену публікацію та оновіть її до очікування (або чернетки чи що завгодно), якщо він не відповідає вашим критеріям.

Альтернативною стратегією було б підключити, wp_insert_post_dataщоб встановити post_status безпосередньо. Недоліком цього методу, що стосується мене, є те, що ви ще не вставили постмета в базу даних, тому вам доведеться обробити її тощо на місці, щоб зробити перевірки, а потім обробити її знову, щоб вставити це в базу даних ... що може стати великою накладною витратою, як за продуктивністю, так і за кодом.


На даний момент я підключаюсь save_postдо пріоритету 1, щоб зберегти метаполя з метабокси; що ви пропонуєте тоді, це мати другий гачок, save_postз пріоритетом, скажімо, 99? Це забезпечило б цілісність? Що робити, якщо з якоїсь причини запускається перший гак, метадані вставляються та публікація публікується, але другий гак не робить, тож ви закінчуєте недійсні поля?
englebip

Я не можу придумати ситуацію, коли перший гак вистрілив би, але не другий ... Який сценарій ти думаєш, що це може спричинити? Якщо вас це турбує, ви можете вставити мета мета публікації, перевірити мета публікації, а потім оновити функцію " post_statusвсе в одну", що закінчується одним викликом, до гачка, якщо ви хочете.
mor7ifer

Я розмістив свій код як редагування свого питання; Я намагався використати другий гак, save_postале це запускає нескінченну петлю.
englebip

Ваше питання полягає в тому, що ви повинні перевірити створену публікацію. Отже if( get_post_status( $post_id ) == 'publish' ), ви хочете використовувати, оскільки ви будете переробляти дані $wpdb->posts, а не дані в $_POST[].
mor7ifer

0

Найкращим методом може бути 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

Вибачте, я не можу дати вам прямо відповідь, але я пам'ятаю, що робив щось подібне зовсім недавно, я просто не можу точно пригадати, як. Я думаю, що я, можливо, зробив це приблизно так - щось на кшталт того, як у мене було значення за замовчуванням, і якщо людина не змінила його, я підняв це у заяві if - так, if(category==default category) {echo "You didn't pick a category!"; return them to the post creation page; }вибачте, це не прямий відповідь, але сподівання це трохи допомагає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.