Добавить проверку и обработку ошибок при сохранении пользовательских полей?


27

У меня есть функция, которая определяет настраиваемое поле для типа сообщения. Скажите, что поле "subhead".

Когда сообщение сохранено, я хочу выполнить некоторую проверку ввода и отобразить сообщение об ошибке на экране редактирования сообщения, если это необходимо. Что-то типа:

// Handle post updating
function wpse_update_post_custom_values($post_id, $post) {

    // Do some checking...
    if($_POST['subhead'] != 'value i expect') {

        // Add an error here
        $errors->add('oops', 'There was an error.');

    }

    return $errors;

} 
add_action('save_post','wpse_update_post_custom_values',1,2);

Я пытаюсь подключить это к действию save_post, но я не могу понять, как обрабатывать ошибки. Похоже, что в функцию не передан объект ошибки, и если я создаю свой собственный объект WP_Error obj и возвращаю его, он не учитывается каким-либо механизмом, который выдает ошибки на странице редактирования сообщения.

В настоящее время у меня есть сообщение об ошибке на странице внутри моего настраиваемого мета-блока, но это не совсем идеально - я бы предпочел большую, красную ошибку "вверху вверх", которую обычно отображает WP.

Любые идеи?

ОБНОВИТЬ:

Основываясь на ответе @Denis, я попробовал несколько разных вещей. Хранение ошибок как глобальных не сработало, потому что Wordpress выполняет перенаправление во время процесса save_post, который убивает глобальные, прежде чем вы можете отобразить их.

Я закончил хранить их в метаполе. Проблема в том, что вам нужно их очистить, иначе они не исчезнут при переходе на другую страницу, поэтому мне пришлось добавить еще одну функцию, прикрепленную к admin_footer, которая просто очищает ошибки.

Я бы не ожидал, что обработка ошибок для чего-то такого распространенного (обновление сообщений) будет такой неуклюжей. Я что-то упускаю очевидное или это лучший подход?

// Handle post updating
function wpse_5102_update_post_custom_values($post_id, $post) {

    // To keep the errors in
    $errors = false;

    // Do some validation...
    if($_POST['subhead'] != 'value i expect') {

        // Add an error here
        $errors .= 'whoops...there was an error.';

    }

    update_option('my_admin_errors', $errors);

    return;

} 
add_action('save_post','wpse_5102_update_post_custom_values',1,2);


// Display any errors
function wpse_5102_admin_notice_handler() {

    $errors = get_option('my_admin_errors');

    if($errors) {

        echo '<div class="error"><p>' . $errors . '</p></div>';

    }   

}
add_action( 'admin_notices', 'wpse_5102_admin_notice_handler' );


// Clear any errors
function wpse_5102__clear_errors() {

    update_option('my_admin_errors', false);

}
add_action( 'admin_footer', 'wpse_5102_clear_errors' );

Хороший вопрос. Я думаю, что вы могли бы избавиться от admin_footerловушки, если очистите ошибки в конце своей функции обработчика уведомлений. Упрощает вещи немного.
Герт

Как вы справляетесь с повторным заполнением полей формы (с возможными неверными данными)?
Герт

У меня есть основной вопрос. В каком файле WordPress PHP?

@Karen Это будет в пользовательском файле плагина или в вашем functions.php.
MathSmath

Возможно, я упускаю что-то очевидное, но будет ли немного эффективнее запускать update_option('my_admin_errors', false);сразу после оператора if в конце wpse_5102_admin_notice_handler()?
Андрей Одри

Ответы:


6

Храните ошибки в своем классе или как глобальные, возможно, в переходном процессе или мета, и отображайте их в уведомлениях администратора по запросам POST. WP не имеет никакого обработчика флеш-сообщений.


Спасибо, что указали мне в этом направлении! Я закончил тем, что использовал мета для хранения ошибок, потому что у меня были проблемы, пытаясь сделать это как глобальное или свойство. Я обновляю свой ответ прямо сейчас, чтобы объяснить, как я это делаю ... пожалуйста, дайте мне знать, если вы предлагаете такую ​​вещь или есть лучший способ, которого я не получаю.
MathSmath

Такого рода вещи, да. Может быть, сохранить его в переменной сеанса, если подумать. Это позволяет нескольким авторам редактировать посты одновременно. :-) Кроме того, я считаю, что невозможно сохранить false в опции. Вместо этого сохраните пустую строку.
Дени де Бернарди

6

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

Сессии не запускаются WordPress. Так что вам нужно начать сеанс в вашем плагине, functions.php или даже wp-config.php:

if (!session_id())
  session_start();

При сохранении сообщения добавляйте ошибки и уведомления в сеанс:

function my_save_post($post_id, $post) {
   if($something_went_wrong) {
     //Append error notice if something went wrong
     $_SESSION['my_admin_notices'] .= '<div class="error"><p>This or that went wrong</p></div>';
     return false; //might stop processing here
   }
   if($somthing_to_notice) {  //i.e. successful saving
     //Append notice if something went wrong
     $_SESSION['my_admin_notices'] .= '<div class="updated"><p>Post updated</p></div>';
   }

   return true;
} 
add_action('save_post','my_save_post');

Распечатайте уведомления и ошибки, а затем очистите сообщения в сеансе:

function my_admin_notices(){
  if(!empty($_SESSION['my_admin_notices'])) print  $_SESSION['my_admin_notices'];
  unset ($_SESSION['my_admin_notices']);
}
add_action( 'admin_notices', 'my_admin_notices' );

исправление для версии сеанса: при первом использовании переменная сеанса не используется. = only = если вы включаете отладку, вы можете проверить почему ...

3
Я тоже этим занимаюсь, но если вы выпустите плагин для такой широкой аудитории, люди будут ненавидеть вас за это. Wordpress не создает экземпляры сессий, потому что он спроектирован так, что он не содержит состояний и не нуждается в них, а некоторые странные настройки сервера могут его сломать. Используйте API для переходных процессов - codex.wordpress.org/Transients_API вместо сессий, и вы сохраните совместимость. Просто подумал, что стоит указать причину, почему бы не сделать это здесь.
Поспи

@pospi, похоже, имеет те же проблемы, что и первоначальное использование функций get_option и update_option. Итак, я думаю, что решение было бы добавить идентификатор текущего пользователя к ключу?
Газиллион

Да, это будет полностью работать! Пока вы добавляете что-то для уникальной идентификации пользователя, вы не будете путать сообщения между зарегистрированными пользователями (:
pospi

5

На основе pospi «s предложение , чтобы использовать переходные , я придумал следующее. Единственная проблема в том, что нет никакого крючка, чтобы поместить сообщение под тем, h2куда идут другие сообщения, поэтому мне пришлось сделать хак jQuery, чтобы получить его там.

Сначала сохраните сообщение об ошибке, используя ваш save_post(или аналогичный) обработчик. Я даю ему короткое время жизни, равное 60 секундам, поэтому оно достаточно долго, чтобы произошло перенаправление.

if($has_error)
{
  set_transient( "acme_plugin_error_msg_$post_id", $error_msg, 60 );
}

Затем просто получите это сообщение об ошибке при загрузке следующей страницы и отобразите его. Я также удаляю его, чтобы он не отображался дважды.

add_action('admin_notices', 'acme_plugin_show_messages');

function acme_plugin_show_messages()
{
  global $post;
  if ( false !== ( $msg = get_transient( "acme_plugin_error_msg_{$post->ID}" ) ) && $msg) {
    delete_transient( "acme_plugin_error_msg_{$post->ID}" );
    echo "<div id=\"acme-plugin-message\" class=\"error below-h2\"><p>$msg</p></div>";
  }
}

Поскольку admin_noticesсрабатывает до того, как сгенерировано содержимое основной страницы, уведомление не о том, куда отправляются другие сообщения редактирования, поэтому мне пришлось использовать этот jQuery, чтобы переместить его туда:

jQuery('h2').after(jQuery('#acme-plugin-message'));

Поскольку идентификатор сообщения является частью временного имени, это должно работать в большинстве многопользовательских сред, за исключением случаев, когда несколько пользователей одновременно редактируют одно и то же сообщение.


Не могли бы вы уточнить «Поскольку идентификатор сообщения является частью временного имени»? Я создал класс для обработки сообщений об ошибках, используя эту технику, но мне требуется, чтобы мой конструктор передавал user_ID. Использует ли временный API user_id при хешировании ключа? (Я спрашиваю, потому что кодекс, кажется, не упоминает об этом)
Gazillion

Нет, но вы можете добавить его вручную. В коде, который я разместил выше, имя переходного процесса acme_plugin_error_msg_POSTID. Вы можете просто добавить идентификатор пользователя к этому как acme_plugin_error_msg_POSTID_USERID.
Джошуа Коуди

2

При save_postзапуске он уже сохранил сообщение в базе данных.

Что касается основного кода WordPress, а точнее wp-includes/post.php- update_post()функции, то нет встроенного способа перехватить запрос перед его сохранением в базе данных.

Тем не менее, мы можем подключить pre_post_updateи использовать header()и get_post_edit_link()предотвратить сохранение сообщения.

<?php

/**
*   Performs validation before saving/inserting custom post type
*/
function custom_post_site_save($post_id, $post_data) {
    // If this is just a revision, don't do anything.
    if (wp_is_post_revision($post_id))
        return;

    if ($post_data['post_type'] == 'my_custom_post_type') {
        // Deny post titles with less than 5 characters
        if (strlen($post_data['post_title'] < 5)) {
            header('Location: '.get_edit_post_link($post_id, 'redirect'));
            exit;
        }
    }
}
add_action( 'pre_post_update', 'custom_post_site_save', 10, 2);

Если вы хотите уведомить пользователя, что пошло не так, проверьте эту суть: https://gist.github.com/Luc45/09f2f9d0c0e574c0285051b288a0f935


Спасибо за это, отлично справляется с проверкой, будь то публикация в первый раз или обновление поста. Вы просто сэкономили мне много времени и сил.
Заде

1

Почему бы вам не проверить свое поле с помощью некоторого Javascript? Я думаю, что это будет лучшим подходом для этого.


Спасибо за предложение! То, что я оставил вне вопроса (для простоты), это то, что я пытаюсь обрабатывать ошибки загрузки файлов, поэтому это должно быть на стороне сервера. Спасибо за предложение, хотя!
MathSmath

Проверка javascript не предотвращает некоторые атаки, проверка на стороне сервера является единственной безопасной. Кроме того, WordPress предлагает несколько хороших инструментов для проверки пользовательских данных. Но вы правы, если просто проверить некоторые значения перед отправкой данных на сервер, вы можете сэкономить время на низком сервере ^^
nderambure

1

Пытаясь использовать приведенный выше скрипт, я столкнулся со странной проблемой. Два сообщения отображаются на экране редактирования после обновления. Один показывает состояние содержимого из предыдущего сохранения, а другой - из текущего. Например, если я правильно сохраню сообщение, а затем сделаю ошибку, первая - «ошибка», а вторая - «хорошо» - хотя они создаются одновременно. Если я изменяю сценарий и добавляю только одно сообщение (например, «ошибка»), инициирую одно обновление с «ошибкой», а после этого другое с «ок», сообщение об ошибке остается (отображается во второй раз). Я должен сохранить "ОК" еще раз, чтобы избавиться от него. Я действительно не знаю, что не так, я проверил это на трех разных локальных серверах, и на каждом из них есть одна и та же проблема.


Я провел еще несколько тестов более простой, второй версии скрипта, о которой я упоминал выше, и кажется, что если сообщение об ошибке действительно добавляется в массив сеансов, оно отображается на экране редактирования. Если сообщения нет (все в порядке) и предыдущее сообщение было ошибкой, оно появляется на экране. Что странно, он генерируется во время сохранения (не кэшируется) - я проверил это с помощью date () в теле сообщения об ошибке. Я сейчас совершенно сбит с толку.
Jlub

Хорошо, в случае, если кто-то еще вырывает волосы из его головы - оказалось, что проблема была в ревизионной системе Wordpress (возможно, какая-то ошибка?). Я отключил его, и теперь все просто отлично.

0

Я написал плагин, который добавляет флэш-обработку ошибок для экранов редактирования сообщений и предотвращает публикацию сообщений до тех пор, пока не будут заполнены обязательные поля:

https://github.com/interconnectit/required-fields

Он позволяет сделать любые поля сообщений обязательными, но вы можете использовать API, который он предоставляет, чтобы сделать любые настраиваемые поля обязательными с настраиваемым сообщением об ошибке и функцией проверки. По умолчанию проверяется, является ли поле пустым или нет.


Не стесняйтесь добавлять любые проблемы на GitHub, если вы сталкиваетесь с ними. Мне нужно немного лучше документировать API, поскольку есть несколько дополнительных фильтров, которые вы можете использовать.
sanchothefat
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.