Статьи

Создание системы обновлений с управлением лицензиями: плагин диспетчера лицензий

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

Но как насчет обновлений?

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

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

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

При создании плагина в этой первой части вы узнаете о следующих методах разработки плагинов WordPress:

  • Использование WordPress Plugin Boilerplate, чтобы начать быстро.
  • Создание нового пользовательского типа записи с мета-блоком для хранения пользовательских метаданных.
  • Создание таблицы базы данных для вашего плагина.
  • Добавление элементов в эту таблицу базы данных.
  • Вывод списка элементов с использованием собственной реализации таблицы списков WordPress.

Если вы хотите увидеть плагин в действии, вы можете загрузить самую последнюю версию плагина с WordPress.org . Точный исходный код, использованный в этом руководстве, можно найти в связанном репозитории Tuts + Github (справа). Мы пройдем каждый шаг по созданию плагина один за другим, но если вы не хотите создавать все с нуля, следуйте вместе с исходным кодом — хороший способ организовать ваше обучение.

Теперь приступим к работе.

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

Но есть и лучший способ.

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

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

Самый простой способ скачать шаблон плагина — это посетить веб-сайт проекта и загрузить последнюю версию в виде zip-файла.

После того, как вы загрузили и распаковали zip-пакет, найдите в нем папку СЛ, скопируйте ее в нужное место и переименуйте в wp-license-manager .

Затем пришло время просмотреть файлы шаблонов плагинов и сделать их собственными:

  1. Просмотрите все файлы в каталоге подключаемых модулей (и его подкаталогах), переименовав все вхождения имени plugin-name в wp-license-manager и Plugin_Name в Wp_License_Manager . Убедитесь, что вы переименовали имена классов и содержащие их файлы. Это займет немного времени и ручной работы, поэтому хорошая идея — использовать PHP IDE для помощи в переименовании.
  2. Добавьте информацию о плагине в файл начальной загрузки плагина, который теперь называется wp‑license-manager.php и находится в корневом каталоге плагина. Вы найдете информацию в блоке комментариев в верхней части файла, сразу после тега PHPDoc @wordpress-plugin .

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

Если вы хотите узнать больше об использовании шаблона, ознакомьтесь с серией учебных пособий Тома Макфарлина « Разработка плагинов с помощью шаблонов WordPress» . Он основан на более ранней версии шаблона, но все же даст вам более глубокое понимание идей, лежащих в основе шаблона плагинов и работы с ним.

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

Вот что вы будете строить в этом разделе:

Скриншот плагина WordPress

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

  • Версия : номер версии продукта. Это поле будет использоваться для проверки обновлений во второй и третьей частях этой серии руководств.
  • Папка для файла : корзина Amazon S3, в которой хранится файл.
  • Имя файла : имя файла почтового индекса продукта, хранящегося в Amazon S3. Мы поговорим об этих параметрах файла во второй части серии.
  • Протестировано с версией WordPress , Требуется версия WordPress , Последнее обновление , Высокое знамя и Низкое знамя : эти параметры показаны в пользовательском интерфейсе обновления плагина в третьей части серии руководств.

Теперь давайте создадим это, создав собственный тип записи и добавив к нему мета-поле.

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

Итак, найдите функцию define_public_hooks внутри класса Wp_License_Manager в Wp_License_Manager includes/class-wp-license-manager.php и добавьте следующее новое действие сразу после двух уже существующих действий wp_enqueue_scripts :

1
$this->loader->add_action( ‘init’, $plugin_public, ‘add_products_post_type’ );

Эта строка использует шаблонный загрузчик действий и фильтров для добавления обработчика действий в init действия WordPress. Функция add_products_post_type входит в класс Wp_License_Manager_Public .

Вот вся функция:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 * Register the new «products» post type to use for products that
 * are available for purchase through the license manager.
 */
public function add_products_post_type() {
    register_post_type( ‘wplm_product’,
        array(
            ‘labels’ => array(
                ‘name’ => __( ‘Products’, $this->plugin_name ),
                ‘singular_name’ => __( ‘Product’, $this->plugin_name ),
                ‘menu_name’ => __( ‘Products’, $this->plugin_name ),
                ‘name_admin_bar’ => __( ‘Products’, $this->plugin_name ),
                ‘add_new’ => __( ‘Add New’, $this->plugin_name ),
                ‘add_new_item’ => __( ‘Add New Product’, $this->plugin_name ),
                ‘edit_item’ => __( ‘Edit Product’, $this->plugin_name ),
                ‘new_item’ => __( ‘New Product’, $this->plugin_name ),
                ‘view_item’ => __( ‘View Product’, $this->plugin_name ),
                ‘search_item’ => __( ‘Search Products’, $this->plugin_name ),
                ‘not_found’ => __( ‘No products found’, $this->plugin_name ),
                ‘not_found_in_trash’ => __( ‘No products found in trash’, $this->plugin_name ),
                ‘all_items’ => __( ‘All Products’, $this->plugin_name ),
            ),
            ‘public’ => true,
            ‘has_archive’ => true,
            ‘supports’ => array( ‘title’, ‘editor’, ‘author’, ‘revisions’, ‘thumbnail’ ),
            ‘rewrite’ => array( ‘slug’ => ‘products’ ),
            ‘menu_icon’ => ‘dashicons-products’,
        )
    );
}

Давайте рассмотрим функцию и то, что она делает — другими словами, параметры функции WordPress register_post_type .

Первый параметр, $post_type , ( строка 6 ) определяет идентификатор типа записи, используемого в ссылках администратора WordPress и wplm_product к сообщениям этого типа (я использовал wplm_product чтобы он не wplm_product с wplm_product созданными другими плагинами и темами). ,

Второй параметр ( строки 7–28 ) — это массив, который определяет свойства типа записи (полный список опций, которые вы можете использовать, смотрите в Кодексе WordPress ):

  • labels определяет набор строковых меток, используемых для ссылки на тип поста в административной области WordPress.
  • public определяет видимость постов этого нового типа. Я хотел, чтобы люди, посещающие сайт, могли просматривать продукты, поэтому установил для этого значение true . Если, с другой стороны, вы хотите сохранить продукты в секрете, просто установите для этого параметра значение false .
  • has_archive определяет, обслуживает ли WordPress страницу архива для перечисления сообщений этого типа или нет.
  • supports определяет, какие функции редактирования сообщений отображаются в редакторе сообщений.
  • rewrite определяет, как будут выглядеть постоянные ссылки для этого типа продукта.
  • menu_icon определяет значок, используемый для типа сообщения в меню администратора. Посетите сайт разработчика WordPress для получения полного списка доступных значков панели инструментов .

Перечисление, добавление и редактирование продуктов теперь все обрабатываются WordPress. Нам осталось добавить мета-поле с пользовательскими настройками, перечисленными выше.

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

Вот как будет выглядеть мета-поле:

Мета-бокс для информации о наличии

Сначала давайте выберем действие для добавления мета-блока. add_meta_boxes_{post_type} — отличный выбор: просто добавьте тип записи вместо {post_type} и ваше действие будет {post_type} раз, когда пришло время добавить мета-блоки для записи данного типа (в нашем случае, wplm_product ).

Поскольку мы работаем над областью администратора, добавьте следующий код в функцию define_admin_hooks() внутри основного класса плагина Wp_License_Manager :

1
$this->loader->add_action( ‘add_meta_boxes_wplm_product’, $plugin_admin, ‘add_product_information_meta_box’ );

Функция add_product_information_meta_box() в Wp_License_Manager_Admin просто определяет мета-поле и функцию рендеринга для отображения его содержимого:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/**
 * Registers a meta box for entering product information.
 * shown in the post editor for the «product» post type.
 *
 * @param $post WP_Post The post object to apply the meta box to
 */
public function add_product_information_meta_box( $post ) {
    add_meta_box(
        ‘product-information-meta-box’,
        __( ‘Product Information’, $this->plugin_name ),
        array( $this, ‘render_product_information_meta_box’ ),
        ‘wplm_product’,
        ‘side’
    );
}

Функция содержит просто вызов функции WordPress add_meta_box , которая определит мета-блок. Параметры в порядке появления следующие:

  1. $id : значение поля HTML id для элемента meta box.
  2. $title : заголовок мета-блока, отображаемый в верхней части окна.
  3. $callback : функция, которая будет выполнять рендеринг метабокса.
  4. $post_type : тип публикации в редакторе страниц, на котором должен отображаться этот мета-блок.
  5. $context : место мета-бокса («обычный», «расширенный» или «боковой»).

Затем добавьте функцию рендеринга, которую мы определили в третьем параметре:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * Renders the product information meta box for the given post (wplm_product).
 *
 * @param $post WP_Post The WordPress post object being rendered.
 */
public function render_product_information_meta_box( $post ) {
    $product_meta = get_post_meta( $post->ID, ‘wp_license_manager_product_meta’, true );
 
    if ( ! is_array( $product_meta ) ) {
        $product_meta = array(
            ‘file_bucket’ => »,
            ‘file_name’ => »,
            ‘version’ => »,
            ‘tested’ => »,
            ‘requires’ => »,
            ‘updated’ => »,
            ‘banner_low’ => »,
            ‘banner_high’ => »
        );
    }
 
    $this->render_nonce_field( ‘product_meta_box’ );
 
    require( ‘partials/product_meta_box.php’ );
}

Строка 7: функция начинается с чтения текущих метаданных.

Строки 9–20 : если метаданные пусты (на самом деле, на данный момент …), создайте массив метаданных по умолчанию с пустыми значениями.

Строка 22: Распечатайте одноразовое поле, которое будет использоваться для дополнительной безопасности при сохранении метаданных. render_nonce_field — это вспомогательная функция, которую я создал, чтобы помочь мне запомнить имя одноразового номера. Мы добавим это в ближайшее время.

Строка 24: включить фактический мета-блок HTML. Каталог partials является частью шаблона плагина WordPress и предназначен для отделения HTML-кода от PHP. Вот как должен выглядеть файл шаблона для мета-блока:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?php
/**
 * The view for the plugin’s product meta box.
 * entering additional product information (version, file bucket, file name).
 *
 * @package Wp_License_Manager
 * @subpackage Wp_License_Manager/admin/partials
 */
?>
<p>
    <label for=»wp_license_manager_product_version»>
        <?php _e( ‘Version:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_version»
           name=»wp_license_manager_product_version»
           value=»<?php echo esc_attr( $product_meta[‘version’] ); ?>»
           size=»25″ >
</p>
<p>
    <label for=»wp_license_manager_product_tested»>
        <?php _e( ‘Tested with WordPress version:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_tested»
           name=»wp_license_manager_product_tested»
           value=»<?php echo esc_attr( $product_meta[‘tested’] ); ?>»
           size=»25″ >
</p>
<p>
    <label for=»wp_license_manager_product_requires»>
        <?php _e( ‘Requires WordPress version:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_requires»
           name=»wp_license_manager_product_requires»
           value=»<?php echo esc_attr( $product_meta[‘requires’] ); ?>»
           size=»25″ >
</p>
<p>
    <label for=»wp_license_manager_product_updated»>
        <?php _e( ‘Last updated:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_updated»
           name=»wp_license_manager_product_updated»
           value=»<?php echo esc_attr( $product_meta[‘updated’] ); ?>»
           size=»25″ >
</p>
 
<p>
    <label for=»wp_license_manager_product_banner_low»>
        <?php _e( ‘Banner low:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_banner_low»
           name=»wp_license_manager_product_banner_low»
           value=»<?php echo esc_attr( $product_meta[‘banner_low’] ); ?>»
           size=»25″ >
</p>
 
<p>
    <label for=»wp_license_manager_product_banner_high»>
        <?php _e( ‘Banner high:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_banner_high»
           name=»wp_license_manager_product_banner_high»
           value=»<?php echo esc_attr( $product_meta[‘banner_high’] ); ?>»
           size=»25″ >
</p>
 
<h3>Download</h3>
 
<p>
    <label for=»wp_license_manager_product_bucket»>
        <?php _e( ‘Amazon S3 Bucket:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_bucket»
           name=»wp_license_manager_product_bucket»
           value=»<?php echo esc_attr( $product_meta[‘file_bucket’] ); ?>»
           size=»25″ />
</p>
<p>
    <label for=»wp_license_manager_product_file_name»>
        <?php _e( ‘Amazon S3 File Name:’, $this->plugin_name );
    </label>
    <input type=»text» id=»wp_license_manager_product_file_name»
           name=»wp_license_manager_product_file_name»
           value=»<?php echo esc_attr( $product_meta[‘file_name’] ); ?>»
           size=»25″ />
</p>

Шаблон представляет собой довольно простой HTML, последовательность меток, за которой следуют элементы ввода со значениями, считанными из массива $product_meta мы получили в функции выше.

Теперь, когда мета-блок создан, давайте позаботимся о сохранении его данных.

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

Это верно и для сохранения данных мета-блока.

На этот раз действие, к save_post нужно подключиться, — это save_post , действие, которое активируется всякий раз, когда запись сохраняется в администраторе WordPress. Опять же, в классе Wp_License_Manager и его функции define_admin_hooks() добавьте новую строку:

1
$this->loader->add_action( ‘save_post’, $plugin_admin, ‘save_product_information_meta_box’ );

Функция save_product_information_meta_box в класс Wp_License_Manager_Admin :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
 * Saves the product information meta box contents.
 *
 * @param $post_id int The id of the post being saved.
 */
public function save_product_information_meta_box( $post_id ) {
    if ( ! $this->is_nonce_ok( ‘product_meta_box’ ) ) {
        return $post_id;
    }
 
    // Ignore auto saves
    if ( defined( ‘DOING_AUTOSAVE’ ) && DOING_AUTOSAVE ) {
        return $post_id;
    }
 
    // Check the user’s permissions
    if ( !current_user_can( ‘edit_posts’, $post_id ) ) {
        return $post_id;
    }
 
    // Read, sanitize, and store user input
    $meta = get_post_meta( $post_id, ‘wp_license_manager_product_meta’, true );
    if ( $meta == » ) {
        $meta = array();
    }
 
    $meta[‘file_bucket’] = sanitize_text_field( $_POST[‘wp_license_manager_product_bucket’] );
    $meta[‘file_name’] = sanitize_text_field( $_POST[‘wp_license_manager_product_file_name’] );
    $meta[‘version’] = sanitize_text_field( $_POST[‘wp_license_manager_product_version’] );
    $meta[‘tested’] = sanitize_text_field( $_POST[‘wp_license_manager_product_tested’] );
    $meta[‘requires’] = sanitize_text_field( $_POST[‘wp_license_manager_product_requires’] );
    $meta[‘updated’] = sanitize_text_field( $_POST[‘wp_license_manager_product_updated’] );
    $meta[‘banner_low’] = sanitize_text_field( $_POST[‘wp_license_manager_product_banner_low’] );
    $meta[‘banner_high’] = sanitize_text_field( $_POST[‘wp_license_manager_product_banner_high’] );
 
    // Update the meta field
    update_post_meta( $post_id, ‘wp_license_manager_product_meta’, $meta );
}

Давайте рассмотрим функцию, чтобы увидеть, что она делает:

  • Строки 7–9: Как вы помните, мы добавили одноразовое поле непосредственно перед отображением метаблока продукта. В этой функции мы используем этот одноразовый номер, чтобы убедиться, что человек, который публикует данные, приходит из этой формы (мы скоро добавим функцию is_nonce_ok ).
  • Строки 11–14: не сохраняйте данные метабокса, когда WordPress периодически выполняет автоматическое сохранение. Это связано с тем, что по умолчанию WordPress не передает данные мета-бокса при вызове AJAX для автоматического сохранения, поэтому обновление данных мета-бокса в этот момент может привести к путанице.
  • Строки 16–19: разрешать сохранение данных только в том случае, если пользователь может редактировать сообщения. В будущем следует рассмотреть возможность добавления настраиваемой возможности редактирования продуктов менеджера лицензий.
  • Строки 22-25: прочитать существующие метаданные продукта или создать новый массив данных, если метаданные еще не сохранены ( get_post_meta возвращает пустую строку, когда элемент метаданных не найден, а для третьего параметра установлено значение true ) ,
  • Строки 27–34 : прочитайте представленные данные, выполнив некоторые базовые санитарные процедуры.
  • Строка 37: все в порядке, сохраните данные.

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

Сначала создаем один:

01
02
03
04
05
06
07
08
09
10
11
/**
 * A helper function for creating and rendering a nonce field.
 *
 * @param $nonce_label string An internal (shorter) nonce name
 */
private function render_nonce_field( $nonce_label ) {
    $nonce_field_name = $this->plugin_name .
    $nonce_name = $this->plugin_name .
 
    wp_nonce_field( $nonce_name, $nonce_field_name );
}

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

Затем проверка:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
/**
 * A helper function for checking the product meta box nonce.
 *
 * @param $nonce_label string An internal (shorter) nonce name
 * @return mixed False if nonce is not OK.
 */
private function is_nonce_ok( $nonce_label ) {
    $nonce_field_name = $this->plugin_name .
    $nonce_name = $this->plugin_name .
 
    if ( ! isset( $_POST[ $nonce_field_name ] ) ) {
        return false;
    }
 
    $nonce = $_POST[ $nonce_field_name ];
 
    return wp_verify_nonce( $nonce, $nonce_name );
}

Функция проверки одноразовых номеров использует те же соглашения о присвоении имен, что и вышеупомянутая функция, а затем использует их для извлечения одноразовых номеров из отправленных данных формы ( строки 11–15 ). Затем, если одноразовый номер найден, он использует функцию WordPress wp_verify_nonce чтобы проверить, соответствует ли представленный код сохраненному ( строка 17 ).

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

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

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

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

В WordPress Plugin Boilerplate это легко сделать, предоставив пустую функцию, в которую мы можем начать писать свой код. Функция Wp_License_Manager_Activator в классе Wp_License_Manager_Activator вызывается каждый раз при активации плагина, либо нажав кнопку « Активировать» в меню плагинов, либо когда обновление плагина заканчивается.

Сначала добавьте следующее определение переменной вверху класса:

1
2
3
4
5
6
7
/**
 * The database version number.
 *
 * @access protected
 * @var string $db_version The database version number
 */
protected static $db_version = 1;

Затем обновите функцию активации с помощью следующего кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
 * Code that is run at plugin activation.
 */
public static function activate() {
    // Update database if db version has increased
    $current_db_version = get_option( ‘wp-license-manager-db-version’ );
    if ( ! $current_db_version ) {
        $current_db_version = 0;
    }
 
    if ( intval( $current_db_version ) < Wp_License_Manager_Activator::$db_version ) {
        if ( Wp_License_Manager_Activator::create_or_upgrade_db() ) {
            update_option( ‘wp-license-manager-db-version’, Wp_License_Manager_Activator::$db_version );
        }
    }
}

Вот что делает функция:

Строки 6–9: прочитайте опцию wp-license-manager-db-version . Мы будем использовать эту опцию для хранения версии базы данных плагина, которая в данный момент активна на сайте.

Строка 11: Сравните текущую версию базы данных с версией, определенной в кодовой базе плагина ( $db_version , определено выше). Если номер версии, установленный в коде плагина, больше, чем номер, сохраненный в настройках WordPress, необходимо обновление базы данных.

Строка 12: запустить обновление базы данных.

Строка 13. Если обновление базы данных прошло успешно, обновите параметр wp-license-manager-db-version чтобы он соответствовал версии в коде.

Теперь мы создали систему для отслеживания версий базы данных и можем перейти к созданию таблицы базы данных. Добавьте следующую функцию в Wp_License_Manager_Activator :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * Creates the database tables required for the plugin if
 * they don’t exist.
 *
 * @return bool true if update was successful.
 */
private static function create_or_upgrade_db() {
    global $wpdb;
 
    $table_name = $wpdb->prefix .
         
    $charset_collate = »;
    if ( ! empty( $wpdb->charset ) ) {
        $charset_collate = «DEFAULT CHARACTER SET {$wpdb->charset}»;
    }
    if ( ! empty( $wpdb->collate ) ) {
        $charset_collate .= » COLLATE {$wpdb->collate}»;
    }
 
    $sql = «CREATE TABLE » .
         .
         .
         .
         .
         .
         .
         .
         .
         .
 
    require_once( ABSPATH . ‘wp-admin/includes/upgrade.php’ );
    dbDelta( $sql );
 
    return true;
}

Большая часть этой функции предназначена для создания запроса SQL CREATE TABLE который мы будем использовать для создания таблицы базы данных:

Строка 10: составьте имя для таблицы базы данных.

Строки 12–18: некоторые определения кодировки, которые будут использоваться в окончательном SQL.

Строки 20–29: SQL для запроса CREATE TABLE . Таблица будет иметь следующие столбцы:

  • id : уникальный идентификатор для строки лицензии.
  • product_id : идентификатор продукта, с которым связана лицензия.
  • license_key : строковый лицензионный ключ, используемый в качестве пароля.
  • email : email владельца лицензии, который работает как имя пользователя.
  • valid_until : срок действия лицензии.
  • created_at : отметка времени, когда была создана лицензия.
  • updated_at : метка времени последнего обновления.

Строки 31–32 : Используйте метод обновления базы данных WordPress dbDelta для создания или обновления таблицы базы данных. Обратите внимание, что dbDelta фактически не выполняет запрос CREATE TABLE как есть, а анализирует его и сравнивает с текущей структурой таблицы, внося изменения по мере необходимости.

Итак, позже, если вы решите внести изменения в структуру таблицы, вместо того, чтобы писать новый SQL-запрос, вы просто отредактируете этот запрос CREATE TABLE , обновите параметр $db_version и позволите dbDelta обработать все остальное.

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

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

Теперь, когда мы создали таблицу базы данных для хранения лицензий, давайте создадим страницы меню: « Лицензии » и « Добавить новые ».

Опять же, мы начинаем с подключения к правильному действию в функции define_admin_hooks :

1
$this->loader->add_action( ‘admin_menu’, $plugin_admin, ‘add_licenses_menu_page’ );

Функция действия add_license_menu_page переходит к Wp_License_Manager_Admin и выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * Creates the settings menu and sub menus for adding and listing licenses.
 */
public function add_licenses_menu_page() {
    add_menu_page(
        __( ‘Licenses’, $this->plugin_name ),
        __( ‘Licenses’, $this->plugin_name ),
        ‘edit_posts’,
        ‘wp-licenses’,
        array( $this, ‘render_licenses_menu_list’ ),
        ‘dashicons-lock’,
        ‘26.1’
    );
 
    add_submenu_page(
        ‘wp-licenses’,
        __( ‘Licenses’, $this->plugin_name ),
        __( ‘Licenses’, $this->plugin_name ),
        ‘edit_posts’,
        ‘wp-licenses’,
        array( $this, ‘render_licenses_menu_list’ )
    );
 
    add_submenu_page(
        ‘wp-licenses’,
        __( ‘Add new’, $this->plugin_name ),
        __( ‘Add new’, $this->plugin_name ),
        ‘edit_posts’,
        ‘wp-licenses-new’,
        array( $this, ‘render_licenses_menu_new’ )
    );
}

Давайте внимательнее посмотрим:

Строки 5–13. Создайте страницу меню верхнего уровня с заголовком « Лицензии ». Функция add_menu_page принимает следующие параметры:

  1. $page_title : заголовок страницы меню (значение тега заголовка HTML).
  2. $menu_title : экранный заголовок страницы меню.
  3. $capability : Возможность, необходимая для просмотра страницы. Я использовал edit_posts , но в будущем я, вероятно, рассмотрю возможность добавления собственной возможности вместо нее.
  4. $menu_slug : идентификатор страницы меню, используемый в URL страницы.
  5. $function : функция, которая будет обрабатывать рендеринг этой страницы меню.
  6. $icon_url : это поле можно использовать по-разному, но я решил использовать значки на панели инструментов, как объяснялось ранее в этом руководстве.
  7. $position : размещение пункта меню в меню WordPress. Проблема с использованием этого параметра заключается в том, что если два элемента меню имеют одинаковое значение $position , будет показан только один из них. Согласно документации WordPress, использование десятичного числа, как мы сделали здесь, немного помогает.

Строки 15–22 : добавить подменю для перечисления лицензий. Параметры для add_submenu_page в остальном идентичны параметрам для add_submenu_page , но первый параметр должен быть идентификатором верхнего меню, к которому следует добавить подменю.

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

Строки 24–31 : Второе подменю — это то, с которого мы начнем, страница для добавления новой лицензии.

Чтобы код работал без ошибок, добавьте две пустые функции:

1
2
3
4
5
6
7
public function render_licenses_menu_list() {
 
}
 
public function render_licenses_menu_new() {
 
}

Создав две страницы настроек, давайте добавим функциональность к первой странице « Добавить новую лицензию ».

Страница будет выглядеть так:

Добавить новую страницу лицензии

Как мы определили выше, функция, которая будет выполнять рендеринг для новой страницы Add , называется render_licenses_menu_new . Сама функция проста, большинство ее строк расходуется на заполнение списка продуктов, которые будут использоваться для отображения выпадающего списка:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/**
 * Renders the list add new license menu page using
 * the «licenses_new.php» partial.
 */
public function render_licenses_menu_new() {
    // Used in the «Product» drop-down list in view
    $products = get_posts(
        array(
            ‘orderby’ => ‘post_title’,
            ‘order’ => ‘ASC’,
            ‘post_type’ => ‘wplm_product’,
            ‘post_status’ => ‘publish’,
            ‘nopaging’ => true,
            ‘suppress_filters’ => true
        )
    );
 
    require plugin_dir_path( dirname( __FILE__ ) ) .
}

Строки 7–16 . Получите все опубликованные сообщения нашего пользовательского типа wplm_product .

Строка 18: включить часть, содержащую HTML для этой страницы меню.

И вот что происходит внутри шаблона HTML:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php
/**
 * The view for the admin page used for adding a new license.
 *
 * @package Wp_License_Manager
 * @subpackage Wp_License_Manager/admin/partials
 */
?>
<div class=»wrap»>
    <div id=»icon-edit» class=»icon32 icon32-posts-post»></div>
 
    <h2><?php _e( ‘Add New License’, $this->plugin_name );
    <p>
        <?php
            $instructions = ‘Use this form to manually add a product license.
                .
 
            _e( $instructions, $this->plugin_name );
        ?>
    </p>
 
    <form action=»<?php echo admin_url( ‘admin-post.php’ ); ?>» method=»post»>
        <?php wp_nonce_field( ‘wp-license-manager-add-license’, ‘wp-license-manager-add-license-nonce’ );
        <input type=»hidden» name=»action» value=»license_manager_add_license»>
 
        <table class=»form-table»>
            <tr class=»form-field form-required»>
                <th scope=»row»>
                    <label for=»email»>
                        <?php _e( ‘Email’, $this->plugin_name );
                        <span class=»description»><?php _e( ‘(required)’, $this->plugin_name );
                    </label>
                </th>
                <td>
                    <input name=»email» type=»text» id=»email» aria-required=»true»>
                </td>
            </tr>
            <tr class=»form-field form-required»>
                <th scope=»row»>
                    <label for=»email»>
                        <?php _e( ‘Product’, $this->plugin_name );
                        <span class=»description»><?php _e( ‘(required)’, $this->plugin_name );
                    </label>
                </th>
                <td>
                    <select name=»product» id=»product» aria-required=»true»>
                        <?php foreach ( $products as $product ) : ?>
                            <option value=»<?php echo $product->ID; ?>»><?php echo $product->post_title;
                        <?php endforeach;
                    </select>
                </td>
            </tr>
            <tr class=»form-field form-required»>
                <th scope=»row»>
                    <label for=»valid_until»>
                        <?php _e( ‘Valid until’, $this->plugin_name );
                    </label>
                </th>
                <td>
                    <input name=»valid_until» type=»text» id=»valid_until» aria-required=»false» />
                    <p class=»description»>
                        <?php _e( ‘(Format: YYYY-MM-DD HH:MM:SS / Leave empty for infinite)’, $this->plugin_name );?>
                    </p>
                </td>
            </tr>
        </table>
 
        <p class=»submit»>
            <input type=»submit» name=»add-license» class=»button button-primary»
                   value=»<?php _e( ‘Add License’, $this->plugin_name ); ?>» >
        </p>
    </form>
 
</div>

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

Некоторые строки, на которые вы должны обратить внимание:

Строка 22: когда пользователь отправляет форму, она будет опубликована в admin-post.php .

Строка 23: Как и в случае с мета-блоком, добавьте одноразовый номер, чтобы удостовериться, что кто-то, публикующий добавить лицензию, действительно делает это через эту страницу.

Строка 24: определите скрытое поле с именем action со значением 'license_manager_add_license' . Это поле будет использоваться для добавления функции обработки для отправки формы — мы вернемся к этому позже.

На предыдущем шаге мы создали форму « Добавить новую лицензию ». Сейчас самое время создать функционал для его сохранения.

Вернитесь к функции define_admin_hooks в Wp_License_Manager и добавьте новое действие:

1
$this->loader->add_action( ‘admin_post_license_manager_add_license’, $plugin_admin, ‘handle_add_license’ );

На этот раз мы используем admin_post_{action} , умное действие для обработки пользовательских форм.

Когда форма с скрытым action поля публикуется, WordPress ищет значение поля и использует его для создания вызова соответствующего действия. Значение, которое мы использовали для скрытого поля, было "license_manager_add_license" и поэтому мы можем добавить действие с именем admin_post_license_manager_add_license .

Функция handle_add_license переходит к Wp_License_Manager_Admin :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
 * Handler for the add_license action (submitting
 * the «Add New License» form).
 */
public function handle_add_license() {
    global $wpdb;
 
    if ( ! empty( $_POST )
        && check_admin_referer( ‘wp-license-manager-add-license’,
            ‘wp-license-manager-add-license-nonce’ ) ) {
 
        // Nonce valid, handle data
 
        $email = sanitize_text_field( $_POST[’email’] );
        $valid_until = sanitize_text_field( $_POST[‘valid_until’] );
        $product_id = intval( $_POST[‘product’] );
         
        $license_key = wp_generate_password( 24, true, false );
 
        // Save data to database
        $table_name = $wpdb->prefix .
        $wpdb->insert(
            $table_name,
            array(
                ‘product_id’ => $product_id,
                ’email’ => $email,
                ‘license_key’ => $license_key,
                ‘valid_until’ => $valid_until,
                ‘created_at’ => current_time( ‘mysql’ ),
                ‘updated_at’ => current_time( ‘mysql’ )
            ),
            array(
                ‘%d’,
                ‘%s’,
                ‘%s’,
                ‘%s’,
                ‘%s’,
                ‘%s’
            )
        );
 
        // Redirect to the list of licenses for displaying the new license
        wp_redirect( admin_url( ‘admin.php?page=wp-licenses’ ) );
    }
}

Давайте рассмотрим функцию, чтобы увидеть, что она делает:

Строки 8–10 . Убедитесь, что одноразовый номер добавлен в форму HTML.

Строки 14–16 . Соберите и очистите предоставленные данные лицензии.

Строка 18 : Генерация лицензионного ключа. В этом плагине мы рассматриваем лицензионный ключ как пароль, который всегда используется вместе с адресом электронной почты пользователя, поэтому я решил не требовать, чтобы лицензионные ключи были уникальными. Функция генерации пароля в WordPress — это простой способ создания случайного пароля, вероятно, не самый безопасный вариант за все время, но достаточно хороший.

Строки 21–40 : сохраните данные в нашей таблице базы данных. Первый параметр функции insert в $wpdb — это имя таблицы. Второй — это массив столбцов базы данных и их значений.Третий указывает типы параметров для форматирования запроса ( %sозначает строку и заключенную в кавычки, числовые значения, указанные с %d, остаются без кавычек).

Строка 43 : перенаправить пользователя в список лицензий. Мы построим это дальше.

Теперь, когда мы сохранили некоторые данные для лицензий, пришло время сделать последний шаг в этом руководстве: создание списка для просмотра лицензий.

Страница лицензий в плагине

Я всегда хотел, чтобы мой интерфейс администратора выглядел так же, как и сам администратор WordPress, поэтому при создании этого списка я решил использовать тот же код, который ядро ​​WordPress использует для визуализации своих собственных списков: Wp_List_Tableкласс, описывается как «[b] ase класс для отображения списка элементов в таблице с измененной HTML-структурой» в документации PHPDoc.

Этот класс позволяет легко создавать хорошо выглядящие таблицы списков, такие как та, что вы видите выше в WordPress, но он поставляется с оговоркой:

Разработчики WordPress задокументировали этот класс как @private, что означает, что они оставляют за собой право радикально изменить его в любое время. Другими словами, использование класса является риском и означает, что вам придется тщательно протестировать свой плагин перед любым новым выпуском WordPress, чтобы убедиться, что списки все еще работают.

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

Сначала найдите Wp_List_Tableкласс (он есть wp-admin/includes/class-wp-list-table.php) и скопируйте его в свой собственный проект. Убедитесь, что переименовали класс и файл, чтобы они не конфликтовали с исходным классом из ядра WordPress (например Wp_License_Manager_List_Table).

Затем создайте класс своего собственного класса для расширения этого базового класса. Позвоните в класс Licenses_List_Tableи поместите его в каталог администратора. Затем добавьте следующие require_onceстроки в функцию load_dependenciesв Wp_License_Manager:

1
2
3
4
5
/**
 * The classes responsible for rendering the list of licenses.
 */
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wp-license-manager-list-table.php';
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-licenses-list-table.php';

Откройте вновь созданный класс Licenses_List_Tableи добавьте конструктор, который принимает текстовый домен для локализации в качестве параметра и поле для хранения значения. Этот не нуждается в объяснениях:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
class Licenses_List_Table extends Wp_License_Manager_List_Table {
 
    /**
    * The plugin's text domain.
    *
    * @access private
    * @var string The plugin's text domain. Used for localization.
    */
    private $text_domain;
 
    /**
     * Initializes the WP_List_Table implementation.
     *
     * @param $text_domain string The text domain used for localizing the plugin.
     */
    public function __construct( $text_domain ) {
        parent::__construct();
 
        $this->text_domain = $text_domain;
    }
     
}

Wp_List_Table поставляется с набором функций, которые мы можем переопределить, чтобы определить данные и правила форматирования для списка.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
 * Defines the database columns shown in the table and a
 * header for each column. The order of the columns in the
 * table define the order in which they are rendered in the list table.
 *
 * @return array The database columns and their headers for the table.
 */
public function get_columns() {
    return array(
        'license_key' => __( 'License Key', $this->text_domain ),
        'email' => __( 'Email', $this->text_domain ),
        'product_id' => __( 'Product', $this->text_domain ),
        'valid_until' => __( 'Valid Until', $this->text_domain ),
        'created_at' => __( 'Created', $this->text_domain )
    );
}

Обратите внимание, что ключи в массиве должны совпадать с именами столбцов в таблице базы данных.

Если мы не хотим отображать created_atстолбец, мы можем легко скрыть его с помощью функции get_hidden_columns:

1
2
3
4
5
6
7
8
/**
 * Returns the names of columns that should be hidden from the list table.
 *
 * @return array The database columns that should not be shown in the table.
 */
public function get_hidden_columns() {
    return array( 'created_at' );
}

Определение столбцов, которые можно использовать для сортировки данных списка, выполняется аналогичным образом:

01
02
03
04
05
06
07
08
09
10
11
/**
 * Returns the columns that can be used for sorting the list table data.
 *
 * @return array The database columns that can be used for sorting the table.
 */
public function get_sortable_columns() {
    return array(
        'email' => array( 'email', false ),
        'valid_until' => array( 'valid_until', false )
    );
}

Далее давайте посмотрим на форматирование данных столбца. Форматирование может быть выполнено одним из двух способов: либо путем определения пользовательской функции форматирования с именем, column_{column_name}где column_nameуказано имя столбца, как указано get_columnsвыше, либо с помощью функции визуализации по умолчанию column_default.

Давайте использовать column_defaultдля столбцов, которые просто распечатаны. Функция принимает два параметра: обрабатываемую строку базы данных ( $item) и имя столбца ( $column_name).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/**
 * Default rendering for table columns.
 *
 * @param $item array The database row being printed out.
 * @param $column_name string The column currently processed.
 * @return string The text or HTML that should be shown for the column.
 */
function column_default( $item, $column_name ) {
    switch( $column_name ) {
        case 'email':
        case 'created_at':
            return $item[$column_name];
 
        default:
            break;
    }
 
    return »;
}

Затем давайте рассмотрим пример пользовательских функций вывода. Глядя на исходный код плагина, вы можете видеть, что я создал несколько таких.

Вот пример рендера для valid_untilстолбца:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/**
 * Custom renderer for the valid_until field.
 *
 * @param $item array The database row being printed out.
 * @return string The text or HTML that should be shown for the column.
 */
function column_valid_until( $item ) {
    $valid_until = $item['valid_until'];
 
    if ($valid_until == '0000-00-00 00:00:00') {
        return __( 'Forever', $this->text_domain );
    }
 
    return $valid_until;
}

Этот рендер отображает «навсегда», когда значение поля равно, '0000-00-00 00:00:00'а в противном случае само значение. Вы получаете смысл.

Теперь, когда мы определили параметры рендеринга для списка, давайте добавим некоторые данные в микс. Это сделано в WP_List_Tableфункции prepare_items.

Вот функция:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
 * Populates the class fields for displaying the list of licenses.
 */
public function prepare_items() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'product_licenses';
 
    $columns = $this->get_columns();
    $hidden = $this->get_hidden_columns();
    $sortable = $this->get_sortable_columns();
 
    $this->_column_headers = array( $columns, $hidden, $sortable );
 
    // Pagination
    $licenses_per_page = 20;
    $total_items = $wpdb->get_var( "SELECT COUNT(id) FROM $table_name" );
 
    $offset = 0;
    if ( isset( $_REQUEST['paged'] ) ) {
        $page = max( 0, intval( $_REQUEST['paged'] ) - 1 );
        $offset = $page * $licenses_per_page;
    }
 
    $this->set_pagination_args(
        array(
            'total_items' => $total_items,
            'per_page' => $licenses_per_page,
            'total_pages' => ceil( $total_items / $licenses_per_page )
        )
    );
 
    // Sorting
    $order_by = 'email'; // Default sort key
    if ( isset( $_REQUEST['orderby'] ) ) {
        // If the requested sort key is a valid column, use it for sorting
        if ( in_array( $_REQUEST['orderby'], array_keys( $this->get_sortable_columns() ) ) ) {
            $order_by = $_REQUEST['orderby'];
        }
    }
 
    $order = 'asc'; // Default sort order
    if ( isset( $_REQUEST['order'] ) ) {
        if ( in_array( $_REQUEST['order'], array( 'asc', 'desc' ) ) ) {
            $order = $_REQUEST['order'];
        }
    }
 
    // Do the SQL query and populate items
    $this->items = $wpdb->get_results(
        $wpdb->prepare( "SELECT * FROM $table_name ORDER BY $order_by $order LIMIT %d OFFSET %d", $licenses_per_page, $offset ),
        ARRAY_A );
}

Давайте рассмотрим функцию, чтобы увидеть, что она делает:

Строки 8–12 : собирать заголовок столбца и сортировать информацию, используя функции, которые мы создали выше.

После этого оставшаяся часть касается запросов данных.

Строки 15–22 : рассчитать информацию о нумерации страниц. Если пользователь запросил страницу, параметр pagedсодержит номер страницы (начиная с 1), поэтому мы можем использовать его для вычисления смещения для запроса SQL.

Строки 24–30 . Передайте информацию о нумерации страниц в таблицу списка, чтобы при отображении таблицы отображались правильные кнопки навигации.

Строки 33–46 : Подготовьте столбец сортировки и порядок на основе пользовательского ввода.

Строки 49–51 . Используйте переменные, определенные ранее в функции, для выполнения запроса к базе данных и заполнения $itemsпеременной класса.

И это все: вы создали таблицу с сортировкой и сортировкой для отображения лицензий.

Ранее в этом уроке мы создали пустую функцию для отображения содержимого страницы « Лицензии» . Теперь, когда мы создали таблицу списка, мы можем заполнить эту функцию ( render_licenses_menu_listin Wp_License_Manager_Admin) и, наконец, сделать видимыми лицензии:

1
2
3
4
5
6
7
8
9
/**
 * Renders the list of licenses menu page using the "licenses_list.php" partial.
 */
public function render_licenses_menu_list() {
    $list_table = new Licenses_List_Table( $this->plugin_name );
    $list_table->prepare_items();
 
    require plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/licenses_list.php';
}

Функция сначала создает экземпляр нашего класса, Licenses_List_Tableзатем вызывает его функцию prepare_items(определенную выше) для инициализации данных и, наконец, включает шаблон HTML для визуализации страницы.

Этот шаблон также содержит вызов $list_table->display(), который отображает список на странице:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<?php
/**
 * The view for the admin page used for listing licenses.
 *
 * @package Wp_License_Manager
 * @subpackage Wp_License_Manager/admin/partials
 */
?>
 
<div class=»wrap»>
    <h2>
        ?>
        <a class="add-new-h2" href="<?php echo admin_url( 'admin.php?page=wp-licenses-new' );?>">
            <?php _e( 'Add new', $this->plugin_name ) ?>
        </a>
    </h2>
 
    ?>
</div>

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

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

Увидимся в следующий раз!