Не публиковать запись пользовательского типа записи, если поле метаданных невалидно

25 апр. 2011 г., 16:35:01
Просмотры: 19K
Голосов: 14

У меня есть пользовательский тип записи (CPT) под названием event. Для этого типа есть метабокс с несколькими полями. Я хочу проверять некоторые поля перед публикацией события. Например, если дата события не указана, я хочу показывать информативное сообщение об ошибке, сохранять событие для дальнейшего редактирования, но не допускать его публикации. Является ли статус 'pending' для CPT записи без всей необходимой информации правильным подходом?

Каковы лучшие практики для валидации полей CPT и предотвращения публикации записи, но с сохранением её для дальнейшего редактирования?

Спасибо, Даша

1
Комментарии

Дружеское напоминание, что на этот вопрос всё ещё нужен принятый ответ.. ;) Если ни один из ответов не решил ваш вопрос, пожалуйста, обновите его, добавив комментарии с уточнениями о том, что осталось неясным (или в чём вам может понадобиться помощь, если это уместно).

t31os t31os
28 июл. 2011 г. 12:14:26
Все ответы на вопрос 4
6
15

Вы можете полностью предотвратить сохранение записи с помощью небольших хаков на JQuery и валидировать поля перед сохранением как на стороне клиента, так и на сервере с помощью ajax:

Сначала добавляем наш JavaScript для перехвата события submit/publish и используем его для отправки собственной ajax-функции перед фактической отправкой:

 add_action('wp_print_scripts','my_publish_admin_hook');

function my_publish_admin_hook(){
if (is_admin()){
        ?>
        <script language="javascript" type="text/javascript">
            jQuery(document).ready(function() {
                jQuery('#post').submit(function() {

                    var form_data = jQuery('#post').serializeArray();
                    form_data = jQuery.param(form_data);
                    var data = {
                        action: 'my_pre_submit_validation',
                        security: '<?php echo wp_create_nonce( 'pre_publish_validation' ); ?>',
                        form_data: form_data
                    };
                    jQuery.post(ajaxurl, data, function(response) {
                        if (response.indexOf('True') > -1 || response.indexOf('true') > -1 || response === true ||  response) {
                            jQuery('#ajax-loading').hide();
                            jQuery('#publish').removeClass('button-primary-disabled');
                            return true;
                        }else{
                            alert("пожалуйста, исправьте следующие ошибки: " + response);
                            jQuery('#ajax-loading').hide();
                            jQuery('#publish').removeClass('button-primary-disabled');
                            return false;
                        }
                    });
                    return false;
                });
            });
        </script>
        <?php
    }
}

Затем создаем функцию для фактической валидации:

add_action('wp_ajax_my_pre_submit_validation', 'pre_submit_validation');
function pre_submit_validation(){
    //простая проверка безопасности
    check_ajax_referer( 'pre_publish_validation', 'security' );

    //здесь выполняем вашу валидацию
    //все поля формы находятся в массиве $_POST['form_data']
    //и возвращаем true для отправки: echo 'true'; die();
    //или ваше сообщение об ошибке: echo 'bal bla bla'; die();
}

Вы всегда можете немного изменить этот код, чтобы валидация выполнялась только для вашего типа записи, добавив условную проверку в функцию my_publish_admin_hook для вашего типа записи, а также выполнять валидацию на стороне клиента, но я предпочитаю делать это на сервере.

26 апр. 2011 г. 15:20:59
Комментарии

Нет ли серверного способа сделать это?

Jeff Jeff
5 июл. 2014 г. 00:58:15

Это серверный способ сделать это.

Bainternet Bainternet
5 июл. 2014 г. 07:59:18

Возможно, я что-то не понимаю. Похоже, вы просто используете PHP для рендеринга JavaScript, который выполняет валидацию. Это не серверная валидация. Я не совсем понимаю, как здесь используется pre_submit_validation.

Jeff Jeff
7 июл. 2014 г. 19:42:26

Первый блок my_publish_admin_hook действительно перехватывает отправку записи на стороне клиента, но затем делает AJAX-запрос к серверу (перед официальной отправкой в pre_submit_validation), который выполняет валидацию на стороне сервера.

emc emc
3 мар. 2015 г. 03:46:12

Это всё ещё валидация на стороне клиента, даже если она использует AJAX для проверки. Клиент должен сначала выполнить JavaScript, чтобы вообще произошла какая-либо валидация.

Однако... Я всё равно нашёл этот ответ полезным для предварительной проверки перед отправкой. Спасибо!

cr0ybot cr0ybot
15 авг. 2016 г. 21:35:00

Жаль, если нет нативного для WP способа сделать такую типичную вещь

Fanky Fanky
24 окт. 2019 г. 15:02:11
Показать остальные 1 комментариев
0

Метод состоит из двух шагов: сначала функция для сохранения данных вашего пользовательского метабокса (привязана к save_post), а затем функция для чтения этих новых post_meta (которые вы только что сохранили), их валидации и при необходимости модификации результата сохранения (также привязана к save_post, но после первой). Если валидация не проходит, функция-валидатор фактически изменяет post_status обратно на "pending", тем самым предотвращая публикацию поста.

Поскольку функция save_post вызывается часто, каждая функция содержит проверки, чтобы выполняться только когда пользователь намерен опубликовать пост и только для вашего пользовательского типа записи (mycustomtype).

Я также обычно добавляю пользовательские уведомления, чтобы помочь пользователю понять, почему его пост не был опубликован, но они немного сложны для включения здесь...

Я не тестировал именно этот код, но это упрощенная версия того, что я делал в крупных настройках пользовательских типов записей.

add_action('save_post', 'save_my_fields', 10, 2);
add_action('save_post', 'completion_validator', 20, 2);

function save_my_fields($pid, $post) {
    // не выполняем при автосохранении или при первом создании постов
    if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || $post->post_status == 'auto-draft' ) return $pid;
    // прерываем, если это не наш пользовательский тип
    if ( $post->post_type != 'mycustomtype' ) return $pid;

    // сохраняем post_meta с содержимым пользовательского поля
    update_post_meta($pid, 'mymetafield', $_POST['mymetafield']);
}


function completion_validator($pid, $post) {
    // не выполняем при автосохранении или при первом создании постов
    if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || $post->post_status == 'auto-draft' ) return $pid;
    // прерываем, если это не наш пользовательский тип
    if ( $post->post_type != 'mycustomtype' ) return $pid;

    // инициализируем маркер завершенности (добавьте больше по мере необходимости)
    $meta_missing = false;

    // получаем метаданные для валидации
    $mymeta = get_post_meta( $pid, 'mymetafield', true );
    // просто проверяем, что поле не пустое - вы можете добавить другие проверки...
    if ( empty( $mymeta ) ) {
        $meta_missing = true;
    }

    // при попытке публикации - проверяем завершенность и вмешиваемся при необходимости
    if ( ( isset( $_POST['publish'] ) || isset( $_POST['save'] ) ) && $_POST['post_status'] == 'publish' ) {
        // не разрешаем публикацию, если что-то не заполнено
        if ( $meta_missing ) {
            global $wpdb;
            $wpdb->update( $wpdb->posts, array( 'post_status' => 'pending' ), array( 'ID' => $pid ) );
            // фильтруем URL запроса, чтобы изменить сообщение о публикации
            add_filter( 'redirect_post_location', create_function( '$location','return add_query_arg("message", "4", $location);' ) );
        }
    }
}

Для нескольких полей метабоксов просто добавьте больше маркеров завершенности, получайте больше post_meta и выполняйте больше проверок...

26 апр. 2011 г. 00:24:03
0

Вам необходимо проверять/валидировать значение метаполя через ajax, то есть когда пользователь нажимает кнопку "Опубликовать/Обновить".

Здесь я проверяю, чтобы у товара WooCommerce с метаполем "product_number" не было пустого значения.

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 == 'product' ){
        ?>
        <script language="javascript" type="text/javascript">
            (function($){
                jQuery(document).ready(function() {

                    jQuery('#publish').click(function() {
                        if(jQuery(this).data("valid")) {
                            return true;
                        }

                        //скрываем иконку загрузки, возвращаем кнопку Опубликовать в обычное состояние
                        jQuery('#publishing-action .spinner').addClass('is-active');
                        jQuery('#publish').addClass('button-primary-disabled');
                        jQuery('#save-post').addClass('button-disabled');

                        var data = {
                            action: 'ep_pre_product_submit',
                            security: '<?php echo wp_create_nonce( "pre_publish_validation" ); ?>',
                            'product_number': jQuery('#acf-field-product_number').val()
                        };
                        jQuery.post(ajaxurl, data, function(response) {

                            jQuery('#publishing-action .spinner').removeClass('is-active');
                            if ( response.success ){
                                jQuery("#post").data("valid", true).submit();
                            } else {
                                alert("Ошибка: " + response.data.message );
                                jQuery("#post").data( "valid", false );

                            }
                            //скрываем иконку загрузки, возвращаем кнопку Опубликовать в обычное состояние
                            jQuery('#publish').removeClass('button-primary-disabled');
                            jQuery('#save-post').removeClass('button-disabled');
                        });
                        return false;
                    });
                });
            })(jQuery);
        </script>
        <?php
    }
}

После этого добавляем функцию-обработчик ajax:

add_action('wp_ajax_ep_pre_product_submit', 'ep_pre_product_submit_func');
function ep_pre_product_submit_func() {
    //простая проверка безопасности
    check_ajax_referer( 'pre_publish_validation', 'security' );

    if ( empty( $_POST['product_number'] ) || empty( $_POST['file_attachment'] ) ) {
         $data = array(
            'message' => __('Пожалуйста, введите номер детали и прикрепите спецификацию.'),
        );
        wp_send_json_error( $data );
    }
    wp_send_json_success();
}
1 окт. 2015 г. 14:31:47
0

Хочу добавить, что для чтения переменных из POST-запроса, используя решение Bainternet, вам нужно будет разобрать строку в $_POST['form_data'] с помощью PHP-функции parse_str (просто чтобы сэкономить вам время на поиск).

$vars = parse_str( $_POST['form_data'] );

После этого вы сможете обращаться к каждой переменной просто по имени, например $varname. Например, если у вас есть мета-поле с названием "my_meta", вы сможете получить к нему доступ так:

$vars = parse_str ( $_POST['form_data'] ) 
if ( $my_meta == "something" ) { // сделать что-то }
22 авг. 2011 г. 09:28:00