Статьи

Создание поддерживаемых мета-боксов WordPress: проверка и дезинфекция

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

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

До этого момента мы собрали базовую файловую организацию плагина, а также интерфейс, но на самом деле мы не реализовали функциональность для сохранения информации в базу данных. И если вы не можете сохранить информацию, то плагин мало кому пригодится.

В этом посте мы вернемся к серверному коду и начнем реализовывать функциональность, которая будет:

  1. Убедитесь, что у пользователя есть возможность сохранять метаданные сообщения
  2. Очистить метаданные поста
  3. Сохранить метаданные поста
  4. Проверить и получить метаданные сообщения

Мы получили нашу работу для нас. В этой статье мы рассмотрим первые два шага, а затем в следующем посте мы рассмотрим последние два шага.

Чтобы убедиться, что у пользователя есть возможность публикации для сохранения метаданных публикации, нам необходимо реализовать проверку безопасности в процессе сериализации. Чтобы сделать это, нам нужно воспользоваться значением nonce .

Одноразовый номер — это «число, используемое один раз» для защиты URL-адресов и форм от неправильного использования.

Чтобы добавить его в наш мета-блок, мы можем реализовать функциональность в разметке, которая отвечает за отображение шаблона публикации. Для этого загрузите admin/views/authors-commentary-navigation.php и обновите шаблон так, чтобы он включал вызов wp_nonce_field :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<div id=»authors-commentary-navigation»>
 
    <h2 class=»nav-tab-wrapper current»>
        <a class=»nav-tab nav-tab-active» href=»javascript:;»>Draft</a>
        <a class=»nav-tab» href=»javascript:;»>Resources</a>
        <a class=»nav-tab» href=»javascript:;»>Published</a>
    </h2>
 
    <?php
 
        // Include the partials for rendering the tabbed content
        include_once( ‘partials/drafts.php’ );
        include_once( ‘partials/resources.php’ );
        include_once( ‘partials/published.php’ );
 
        // Add a nonce field for security
        wp_nonce_field( ‘authors_commentary_save’, ‘authors_commentary_nonce’ );
 
    ?>
 
</div>

В приведенном выше коде мы ввели одноразовый номер, который соответствует действию сохранения комментария автора (который мы authors_commentary_nonce ), и связали его со значением, которое идентифицировано authors_commentary .

Посмотрим, где это вступит в игру на мгновение. На данный момент, если вы загрузите свой браузер, вы не увидите ничего нового. Это потому, что одноразовые значения отображаются в скрытом поле.

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

1
<input type=»hidden» id=»authors_commentary_nonce» name=»authors_commentary_nonce» value=»f3cd131d28″>

Конечно, value вашего nonce будет отличаться.

Чтобы убедиться, что у пользователя есть разрешение на сохранение сообщения, мы хотим проверить три вещи:

  1. что пользователь сохраняет информацию для типа сообщения
  2. что пост не сохраняется автоматически в WordPress
  3. что пользователь на самом деле имеет разрешение на сохранение

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

Сначала давайте настроим хук и функцию, которые будут использоваться для использования вспомогательных функций и сохранения метаданных. В конструкторе для Authors_Commentary_Meta_Box добавьте следующую строку кода:

1
<?php add_action( ‘save_post’, array( $this, ‘save_post’ ) );

Далее давайте определим функцию. Обратите внимание, что я выполняю вызовы двух функций в следующем блоке кода. Мы определим их на мгновение:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<?php
/**
 * Sanitizes and serializes the information associated with this post.
 *
 * @since 0.5.0
 *
 * @param int $post_id The ID of the post that’s currently being edited.
 */
public function save_post( $post_id ) {
 
    /* If we’re not working with a ‘post’ post type or the user doesn’t have permission to save,
     * then we exit the function.
     */
    if ( ! $this->is_valid_post_type() || ! $this->user_can_save( $post_id, ‘authors_commentary_nonce’, ‘authors_commentary_save’ ) ) {
        return;
    }
 
}

Учитывая приведенный выше код, мы говорим WordPress save_post нашу функцию save_post всякий раз, когда save_post ее действие save_post . Внутри функции мы говорим: «Если сохраняемый пост не относится к типу поста или если у пользователя нет прав на сохранение, выйдите из функции».

Конечно, нам нужно определить функции так, чтобы логика работала. Сначала мы напишем функцию is_valid_post_type как private функцию текущего класса. Он проверит массив $_POST чтобы убедиться, что тип сохраняемой записи — это фактически запись.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?php
 
/**
 * Verifies that the post type that’s being saved is actually a post (versus a page or another
 * custom post type.
 *
 *
 * @since 0.5.0
 * @access private
 * @return bool Return if the current post type is a post;
 */
private function is_valid_post_type() {
    return !
}

Далее мы добавим функцию user_can_save . Это функция, которая гарантирует, что сообщение не сохраняется в WordPress, и что если пользователь сохраняет функцию, то значение nonce, связанное с действием публикации, будет правильно установлено.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
     
/**
 * Determines whether or not the current user has the ability to save meta data associated with this post.
 *
 * @since 0.5.0
 * @access private
 * @param int $post_id The ID of the post being save
 * @param string $nonce_action The name of the action associated with the nonce.
 * @param string $nonce_id The ID of the nonce field.
 * @return bool Whether or not the user has the ability to save this post.
 */
private function user_can_save( $post_id, $nonce_action, $nonce_id ) {
 
    $is_autosave = wp_is_post_autosave( $post_id );
    $is_revision = wp_is_post_revision( $post_id );
    $is_valid_nonce = ( isset( $_POST[ $nonce_action ] ) && wp_verify_nonce( $_POST[ $nonce_action ], $nonce_id ) );
 
    // Return true if the user is able to save;
    return !
 
}

Обратите внимание, что здесь мы nonce_action и nonce_id которые мы определили в шаблоне на первом шаге. Мы также используем wp_verify_nonce в сочетании с указанной информацией.

Таким образом мы можем убедиться, что сохраняемая запись сделана пользователем, имеющим надлежащие права доступа и разрешения.

Предполагая, что пользователь работает со стандартным типом записей и что он / она имеет разрешение на сохранение информации, нам необходимо очистить данные.

Для этого нам нужно сделать следующее:

  1. Убедитесь, что никакая информация в метаданных публикации не пуста
  2. Уберите все, что может быть опасно, для записи в базу данных.

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

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

После этого вернитесь в функцию save_post .

Поскольку первая вкладка, которая существует внутри мета-блока, — это вкладка « Черновики », мы начнем с нее. Обратите внимание, что это textarea , поэтому логика, которая существует для очистки этой информации, должна быть следующей:

  • удалить любые теги HTML
  • экранировать содержимое текстовой области

Напомним, что textarea называется « authors-commentary-drafts поэтому мы можем получить к ней доступ в массиве $_POST . Для этого мы будем использовать следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
<?php
 
// If the ‘Drafts’ textarea has been populated, then we sanitize the information.
if ( ! empty( $_POST[‘authors-commentary-drafts’] ) ) {
 
    // We’ll remove all white space, HTML tags, and encode the information to be saved
    $drafts = trim( $_POST[‘authors-commentary-drafts’] );
    $drafts = esc_textarea( strip_tags( $drafts ) );
 
    // More to come…
 
}

Проще говоря, мы проверяем, является ли информация в массиве $_POST пустой. Если нет, то мы очистим данные.

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

Во-первых, нам нужно внести одно небольшое изменение в функцию createInputElement которая существует в файле admin/assets/js/resources.js . В частности, нам нужно убедиться, что атрибут name использует массив, чтобы мы могли правильно обращаться к нему и проходить по нему, просматривая данные $_POST .

Убедитесь, что строки кода, отвечающие за создание фактического элемента, выглядят так:

1
2
3
4
5
6
7
// Next, create the actual input element and then return it to the caller
$inputElement =
    $( ‘<input />’ )
        .attr( ‘type’, ‘text’ )
        .attr( ‘name’, ‘authors-commentary-resources[‘ + iInputCount + ‘]’ )
        .attr( ‘id’, ‘authors-commentary-resource-‘ + iInputCount )
        .attr( ‘value’, » );

Обратите внимание, что ключ к тому, что мы сделали, лежит в строке, которая обновляет name . В частности, мы помещаем количество входов в индексы массива.

Затем вернитесь в функцию save_post и добавьте следующий код (который мы обсудим после блока):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?php
 
// If the ‘Resources’ inputs exist, iterate through them and sanitize them
if ( ! empty( $_POST[‘authors-commentary-resources’] ) ) {
 
    $resources = $_POST[‘authors-commentary-resources’];
    foreach ( $resources as $resource ) {
         
        $resource = esc_url( strip_tags( $resource ) );
         
        // More to come…
         
    }
 
}

Поскольку мы работаем с массивом входных данных, нам нужно сначала проверить, что массив не пустой. Если это не так, то нам нужно пройти через это, потому что мы не уверены, сколько входов нам нужно будет обработать.

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

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

С другой стороны, мы имеем дело только с флажком, который имеет логическое значение: проверяется или нет (или, в частности, «включено» или пусто), поэтому дезинфекция информации действительно проста.

Сначала давайте обновим частичное. Найдите admin/views/partials/published.php . Затем найдите строку, которая определяет флажок input и измените ее так, чтобы она выглядела следующим образом:

1
2
3
4
<label for=»authors-commentary-comment-<?php echo $comment->comment_ID ?>»>
    <input type=»checkbox» name=»authors-commentary-comments[<?php echo $comment->comment_ID ?>]» id=»authors-commentary-comment-<?php echo $comment->comment_ID ?>» />
    This comment has received a reply.
</label>

Обратите внимание, что мы изменили атрибут name чтобы он использовал массив с индексом в качестве значения. Далее мы вернемся к функции save_post еще раз, чтобы ввести проверку этого конкретного элемента:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?php
 
// If there are any values saved in the ‘Resources’ input, save them
if ( ! empty( $_POST[‘authors-commentary-comments’] ) ) {
 
    $comments = $_POST[‘authors-commentary-comments’];
    foreach ( $comments as $comment ) {
 
        $comment = strip_tags( stripslashes( $comment ) );
        // More to come…
 
    }
 
}

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

На данный момент мы готовы взять два последних пункта серии:

  1. Сохранение и получение
  2. Рефакторинг

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

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

А пока просмотрите приведенный выше код, ознакомьтесь с источником из GitHub и оставьте все вопросы и комментарии в поле ниже.