Обычный запрос, особенно для тех, кто создал пользовательские типы сообщений, такие как «Новости» или «События», заключается в добавлении ссылки на страницу архива их типа сообщений в их меню навигации. Однако в настоящее время это можно сделать только вручную, введя URL-адрес архива типа записи. Помимо того, что это довольно не элегантное решение, у этого решения есть несколько недостатков: оно не всегда отображается как «текущее», если вы измените структуру постоянной ссылки, это может привести к разрыву ссылки, ручное добавление URL-адресов утомительно, а ссылка не отображается как « current ‘когда на пост такого типа.
В этом уроке я покажу вам, как создать плагин, который создает мета-поле на вашей странице «Внешний вид» -> «Меню», которое позволяет вам добавлять ссылки на архивы пост-типа. Эти ссылки не страдают от недостатков, упомянутых выше.
Шаг 1 Создание плагина
Этот плагин будет называться «Мои архивные ссылки типа поста », и для этого сначала создайте папку my-post-type-archive-links в папке / wp-content / plugins / , а внутри создайте файл my- post-type-archive-links.php . Этот файл является основным файлом плагина. Мы собираемся обернуть его в класс — это просто, чтобы нам не приходилось беспокоиться о конфликтах имен наших функций с WordPress или другими плагинами: нам просто нужно убедиться, что имя нашего класса уникально. Добавьте следующее в my-post-type-archive-links.php
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<?php
/*
Plugin Name: My Post Type Archive Links
Version: 1.0
Description: Adds a metabox to the Appearance -> Menu page to add post type archive links
Author: Stephen Harris
Author URI: http://profiles.wordpress.org/users/stephenh1988/
*/
class My_Post_Type_Archive_Link {
//Everything will go here
}
My_Post_Type_Archive_Link::load();
?>
|
Все в этом уроке будет находиться внутри этого класса.
Шаг 2 Загрузка плагина
Когда файл плагина загружается, он запускает метод класса load() . Этот метод будет отвечать за добавление действий и фильтров на различные хуки WordPress. Мы рассмотрим каждый из них на последующих шагах, но он также предоставляет полезную сводку. Добавьте следующий метод в наш класс:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public function load(){
// Hook function to add the metabox to the Menu page
add_action( ‘admin_init’, array(__CLASS__,’add_meta_box’));
// Javascript for the meta box
add_action( ‘admin_enqueue_scripts’, array(__CLASS__,’metabox_script’) );
// Ajax callback to create menu item and add it to menu
add_action(‘wp_ajax_my-add-post-type-archive-links’, array( __CLASS__, ‘ajax_add_post_type’));
// Assign menu item the appropriate url
add_filter( ‘wp_setup_nav_menu_item’, array(__CLASS__,’setup_archive_item’) );
// Make post type archive link ‘current’
add_filter( ‘wp_nav_menu_objects’, array(__CLASS__,’maybe_make_current’));
}
|
Давайте подведем итог, что делает каждая из этих частей:
- Добавьте метабокс — достаточно понятный. Подключенная функция будет отвечать за добавление нашего мета-блока.
- Ставить JavaScript в очередь — мы используем хук admin_enqueue_scripts, чтобы поставить в очередь наш файл JavaScript. Наш JavaScript, когда нажимают «добавить в меню», вызывает запрос AJAX.
- AJAX callback — эта функция отвечает за обработку вышеуказанного AJAX-запроса. Это создаст пункты меню и добавит их в меню.
- Настройка пункта меню — это гарантирует, что когда ссылка на архив появится в вашем меню, она правильно указывает на тип архива.
- Может быть, сделать текущий — всякий раз, когда появляется меню, его элементы пропускаются через фильтр, мы гарантируем, что класс ‘
current-menu-item‘ будет добавлен к соответствующей ссылке типа сообщения.
Шаг 3 Добавление метабокса
Сначала мы определяем наш метод add_meta_box , который просто вызывает функцию WordPress add_meta_box() . Детали этой функции были рассмотрены много раз прежде, но если вы не уверены, вы можете прочитать об этом на страницах Кодекса .
|
1
2
3
|
public function add_meta_box() {
add_meta_box( ‘post-type-archives’, __(‘Post Types’,’my-post-type-archive-links’),array(__CLASS__,’metabox’),’nav-menus’ ,’side’,’low’);
}
|
Далее мы определяем функцию обратного вызова метабокса, которая отвечает за отображение внутренних частей метабокса:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public function metabox( ) {
global $nav_menu_selected_id;
//Get post types
$post_types = get_post_types(array(‘public’=>true,’_builtin’=>false), ‘object’);?>
<!— Post type checkbox list —>
<ul id=»post-type-archive-checklist»>
<?php foreach ($post_types as $type):?>
<li><label><input type=»checkbox» value =»<?php echo esc_attr($type->name); ?>» /> <?php echo esc_attr($type->labels->name);
<?php endforeach;?>
</ul><!— /#post-type-archive-checklist —>
<!— ‘Add to Menu’ button —>
<p class=»button-controls» >
<span class=»add-to-menu» >
<input type=»submit» id=»submit-post-type-archives» <?php disabled( $nav_menu_selected_id, 0 );
</p>
<?php
}
|
Этот метод просто получает все открытые пользовательские типы get_post_types() с помощью get_post_types() а затем перебирает их, чтобы создать список флажков. Каждый флажок имеет имя типа сообщения в качестве значения. На следующем шаге мы добавим JavaScript, который будет срабатывать, когда пользователь нажимает кнопку «Добавить в меню».
Шаг 4 JavaScript
Мы только хотим включить наш JavaScript на странице Внешний вид -> Меню администратора. Мы использовали хук admin_enqueue_scripts который срабатывает только на страницах администратора и передает хук страницы в качестве аргумента. Хук для страницы Внешний вид -> Меню — nav-menus.php . После постановки в очередь нашего скрипта мы используем wp_localize_script чтобы сделать одноразовый номер доступным в нашем JavaScript. Мы включили его в запрос AJAX, чтобы убедиться, что действие было запланировано.
|
01
02
03
04
05
06
07
08
09
10
|
public function metabox_script($hook) {
if( ‘nav-menus.php’ != $hook )
return;
//On Appearance>Menu page, enqueue script:
wp_enqueue_script( ‘my-post-type-archive-links_metabox’, plugins_url(‘/metabox.js’, __FILE__), array(‘jquery’));
//Add nonce variable
wp_localize_script(‘my-post-type-archive-links_metabox’,’MyPostTypeArchiveLinks’, array(‘nonce’=>wp_create_nonce(‘my-add-post-type-archive-links’)));
}
|
На предыдущем шаге кнопка «Добавить в меню» получила идентификатор submit-post-type-archives . Теперь мы используем jQuery для нацеливания на эту кнопку и, при нажатии, отправляем запрос AJAX, чтобы создать пункт меню и добавить его в меню. Следующее — единственная часть этого урока, которая живет за пределами нашего класса. Он должен находиться в файле с именем metabox.js , в нашей папке плагинов.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
jQuery(document).ready(function($) {
$(‘#submit-post-type-archives’).click(function(event) {
event.preventDefault();
/* Get checked boxes */
var postTypes = [];
$(‘#post-type-archive-checklist li :checked’).each(function() {
postTypes.push($(this).val());
});
/* Send checked post types with our action, and nonce */
$.post( ajaxurl, {
action: «my-add-post-type-archive-links»,
posttypearchive_nonce: MyPostTypeArchiveLinks.nonce,
post_types: postTypes
},
/* AJAX returns html to add to the menu */
function( response ) {
$(‘#menu-to-edit’).append(response);
}
);
})
});
|
Обратите внимание на адрес, по ajaxurl мы отправляем запрос: ajaxurl . Мы не определили это нигде. Это глобальная переменная, установленная WordPress только на стороне администратора, которая указывает на страницу, которую WordPress использует для обработки запросов AJAX. При нажатии кнопки отправки на этот URL-адрес отправляются имена проверенных типов сообщений, уникальных действий и одноразовых номеров. Когда WordPress получает запрос, он запускает wp_ajax_my-add-post-type-archive-links . Одноразовый номер является мерой безопасности, помогающей убедиться, что действие было запланировано.
Шаг 5 Обратный звонок AJAX
Теперь мы определим функцию обратного вызова AJAX ajax_add_post_type .
|
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
|
public function ajax_add_post_type() {
if ( ! current_user_can( ‘edit_theme_options’ ) )
die(‘-1’);
check_ajax_referer(‘my-add-post-type-archive-links’, ‘posttypearchive_nonce’);
require_once ABSPATH .
if(empty($_POST[‘post_types’]))
exit;
// Create menu items and store IDs in array
$item_ids=array();
foreach ( (array) $_POST[‘post_types’] as $post_type) {
$post_type_obj = get_post_type_object($post_type);
if(!$post_type_obj)
continue;
$menu_item_data= array(
‘menu-item-title’ => esc_attr($post_type_obj->labels->name),
‘menu-item-type’ => ‘post_type_archive’,
‘menu-item-object’ => esc_attr($post_type),
‘menu-item-url’ => get_post_type_archive_link($post_type)
);
//Collect the items’ IDs.
$item_ids[] = wp_update_nav_menu_item(0, 0, $menu_item_data );
}
// If there was an error die here
if ( is_wp_error( $item_ids ) )
die(‘-1’);
// Set up menu items
foreach ( (array) $item_ids as $menu_item_id ) {
$menu_obj = get_post( $menu_item_id );
if ( ! empty( $menu_obj->ID ) ) {
$menu_obj = wp_setup_nav_menu_item( $menu_obj );
$menu_obj->label = $menu_obj->title;
$menu_items[] = $menu_obj;
}
}
// This gets the HTML to returns it to the menu
if ( ! empty( $menu_items ) ) {
$args = array(
‘after’ => »,
‘before’ => »,
‘link_after’ => »,
‘link_before’ => »,
‘walker’ => new Walker_Nav_Menu_Edit
);
echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
}
// Finally don’t forget to exit
exit;
}
|
Давайте пройдем этот обратный вызов немного за один раз. Сначала мы проверяем права пользователя, проверяем одноразовый номер и загружаем страницу nav-menu.php (нам нужны некоторые функции).
|
1
2
3
4
5
6
7
8
9
|
if ( ! current_user_can( ‘edit_theme_options’ ) )
die(‘-1’);
check_ajax_referer(‘my-add-post-type-archive-links’,’posttypearchive_nonce’);
require_once ABSPATH .
if(empty($_POST[‘post_types’]))
exit;
|
Затем мы создаем пункт меню для каждого выбранного типа записи. Сначала мы проверяем, существует ли тип записи, который мы получили, проверяя значение, возвращаемое get_post_type_object() . Мы можем получить get_post_type_archive_link() ссылку с помощью функции get_post_type_archive_link()
Пункты меню на самом деле представляют собой посты типа post ‘ nav_menu_item ‘ со встроенной мета-публикацией, включая поля, относящиеся к ‘ url ‘, ‘ type ‘ и ‘ object ‘. Тип элемента обычно равен custom , post_type или taxonomy , но мы установим его значение post_type_archive . Мета-значение элемента ‘ object ‘ обычно используется только для элементов типа ‘ post_type ‘ или ‘ taxonomy ‘ и относится к типу записи или таксономии, на которую ссылается ссылка. Мы будем использовать это для хранения типа записи ссылки на архив.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// Create menu items and store IDs in array
$item_ids=array();
foreach ( (array) $_POST[‘post_types’] as $post_type) {
$post_type_obj = get_post_type_object($post_type);
if(!$post_type_obj)
continue;
$menu_item_data= array(
‘menu-item-title’ => esc_attr($post_type_obj->labels->name),
‘menu-item-type’ => ‘post_type_archive’,
‘menu-item-object’ => esc_attr($post_type),
‘menu-item-url’ => get_post_type_archive_link($post_type)
);
// Collect the items’ IDs.
$item_ids[] = wp_update_nav_menu_item(0, 0, $menu_item_data );
}
// If there was an error die here
if ( is_wp_error( $item_ids ) )
die(‘-1’);
|
Далее мы просто генерируем HTML, который будет добавлен в меню. Мы используем массив $item_ids чтобы получить массив пунктов меню и передать его классу WordPress, чтобы выполнить тяжелую работу за нас.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//Set up menu items
foreach ( (array) $item_ids as $menu_item_id ) {
$menu_obj = get_post( $menu_item_id );
if ( ! empty( $menu_obj->ID ) ) {
$menu_obj = wp_setup_nav_menu_item( $menu_obj );
$menu_obj->label = $menu_obj->title;
$menu_items[] = $menu_obj;
}
}
//This gets the HTML to returns it to the menu
if ( ! empty( $menu_items ) ) {
$args = array(
‘after’ => »,
‘before’ => »,
‘link_after’ => »,
‘link_before’ => »,
‘walker’ => new Walker_Nav_Menu_Edit
);
echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
}
//Finally don’t forget to exit
exit;
|
Шаг 6 Пункт меню
К сожалению, из-за ошибки в WordPress, если тип вашего элемента не « taxonomy », « custom » или « post_type », URL удаляется. Чтобы противостоять этому, когда в меню используется ссылка « post_type_archive », мы вручную повторно добавляем URL. Это также обеспечивает актуальность ссылки на архив (в случае, если ваша структура постоянных ссылок была изменена).
|
1
2
3
4
5
6
7
8
9
|
public function setup_archive_item($menu_item){
if($menu_item->type !=’post_type_archive’)
return $menu_item;
$post_type = $menu_item->object;
$menu_item->url =get_post_type_archive_link($post_type);
return $menu_item;
}
|
Шаг 7 Создание текущей ссылки
Наконец, нам нужно сделать элемент «текущим», когда мы находимся на соответствующей странице. Я хочу, чтобы ссылка на архив типа записи была выделена как текущая, если мы находимся на этой странице архива или просматриваем один пост такого типа. Для этого я проверяю:
Чтобы сделать элемент текущим, нам просто нужно добавить current-menu-item к классам элемента, которые хранятся в $item->classes . Затем мы должны перебрать его родителей в меню и добавить классы current_item_parent и current_item_ancestor . Давайте посмотрим на каждый бит в отдельности:
Мы перебираем все пункты меню:
|
1
2
3
4
5
6
|
public function maybe_make_current($items) {
foreach ($items as $item) {
// This is where we check the item
}
return $items;
}
|
Если элемент не относится к « post_type_archive » или если он есть, но мы не хотим, чтобы он стал «текущим», мы просто переходим к следующему элементу. Напомним, что для наших архивных ссылок тип записи сохраняется как объект элемента. Итак, внутри цикла foreach :
|
1
2
3
4
5
6
|
if(‘post_type_archive’ != $item->type)
continue;
$post_type = $item->object;
if(!is_post_type_archive($post_type)&& !is_singular($post_type))
continue;
|
Если мы хотим сделать это текущим, мы даем ему соответствующий класс и затем выбираем его родителей в меню. Родитель пункта меню сохраняется как мета-пост с мета-ключом _menu_item_menu_item_parent .
|
01
02
03
04
05
06
07
08
09
10
11
|
//Make item current
$item->current = true;
$item->classes[] = ‘current-menu-item’;
//Get menu item’s ancestors:
$_anc_id = (int) $item->db_id;
$active_ancestor_item_ids=array();
while(( $_anc_id = get_post_meta( $_anc_id, ‘_menu_item_menu_item_parent’, true ) ) && ! in_array( $_anc_id, $active_ancestor_item_ids ) ) {
$active_ancestor_item_ids[] = $_anc_id;
}
|
Затем мы перебираем пункты меню и даем родителям и предкам «текущего» класса соответствующие классы.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
// Loop through the items and give ancestors and parents the appropriate class
foreach ($items as $key=>$parent_item) {
$classes = (array) $parent_item->classes;
// If menu item is the parent
if ($parent_item->db_id == $item->menu_item_parent ) {
$classes[] = ‘current-menu-parent’;
$items[$key]->current_item_parent = true;
}
// If menu item is an ancestor
if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) {
$classes[] = ‘current-menu-ancestor’;
$items[$key]->current_item_ancestor = true;
}
$items[$key]->classes = array_unique( $classes );
}
|
Соединяем эту функцию вместе:
|
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
|
public function maybe_make_current($items) {
foreach ($items as $item) {
if(‘post_type_archive’ != $item->type)
continue;
$post_type = $item->object;
if(!is_post_type_archive($post_type)&& !is_singular($post_type))
continue;
// Make item current
$item->current = true;
$item->classes[] = ‘current-menu-item’;
// Get menu item’s ancestors:
$_anc_id = (int) $item->db_id;
$active_ancestor_item_ids=array();
while(( $_anc_id = get_post_meta( $_anc_id, ‘_menu_item_menu_item_parent’, true ) ) && ! in_array( $_anc_id, $active_ancestor_item_ids ) ) {
$active_ancestor_item_ids[] = $_anc_id;
}
// Loop through ancestors and give them ‘ancestor’ or ‘parent’ class
foreach ($items as $key=>$parent_item) {
$classes = (array) $parent_item->classes;
// If menu item is the parent
if ($parent_item->db_id == $item->menu_item_parent ) {
$classes[] = ‘current-menu-parent’;
$items[$key]->current_item_parent = true;
}
// If menu item is an ancestor
if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) {
$classes[] = ‘current-menu-ancestor’;
$items[$key]->current_item_ancestor = true;
}
$items[$key]->classes = array_unique( $classes );
}
}
return $items;
}
|
Осталось только зайти на страницу администрирования плагинов и активировать плагин.
Вывод
Всегда есть возможности для улучшения. Например, с небольшим количеством jQuery вы можете добавить ссылку «Выбрать все» под флажками или отобразить символ «загрузки» во время обработки AJAX. Теперь этот плагин не самое простое решение — но он работает хорошо и избегает ловушек простого добавления пользовательской ссылки. Вышеприведенный плагин целиком можно найти на моем GitHub . Если у вас есть какие-либо комментарии или предложения, не стесняйтесь оставлять комментарии или связываться со мной через Twitter .