[Решено/2 способа] Добавление своего пункта (метабокса) в расширенные свойства меню
На странице редактирования меню и управления областями меню в настройках экрана можно включить показ расширенных свойств меню (Цель ссылки, Атрибут title, Классы CSS, Отношение к ссылке (XFN) и Описание) - рис1.
Как добавить к этим 5-ти пунктам, свой 6-ой пункт, допустим "Активность", чтобы в существующем/добавляемом пункте меню появился чекбокс (как в случае с "Цель ссылки" появляется чекбокс "Открывать в новой вкладке"), допустим "Сделать пункт меню неактивным". И если данный чекбокс будет задействован, то у данного пункта меню автоматически уберётся ссылка - рис2.
Меню выводится стандартной функцией wp_nav_menu().
Пока отделался костылём. В свойствах пункта меню, присваиваю класс unclickable, а в functions прописал хук
function no_link_unclickable_page( $p ) {
return preg_replace( '%((unclickable)[^<]+)[^>]+>([^<]+)</a>%', '$1<a>$3</a>', $p, 1 );
}
add_filter( 'wp_nav_menu', 'no_link_unclickable_page' ); Пока не разобрался ещё как сделать так, как хочется. Сделал немного по-другому. Может кому будет полезно и интересно. Решил зайти с другой стороны.
Альтернативный метод
1 шаг. Берём и добавляем в functions.php следующий код:
/**
* Adds the meta box to the page screen
*/
add_action( 'add_meta_boxes', 'additional_page_attributes_metabox' );
function additional_page_attributes_metabox()
{
add_meta_box(
'additional_page_attributes', // id, used as the html id att
__( 'Сделать пункт меню неактивным' ), // meta box title, like "Page Attributes"
'additional_page_attributes_callback', // callback function, spits out the content
'page', // post type or page. We'll add this to pages only
'side', // context (where on the screen
'core' // priority, where should this go in the context?
);
}
/**
* Callback function for our meta box. Echo out the content
*/
function additional_page_attributes_callback( $post ) {
global $custom_meta_fields, $post;
$desc = 'Сделать пункт меню неактивным?';
$meta = get_post_meta($post->ID, "additional-page-attributes-checkbox", true);
wp_nonce_field(basename(__FILE__), 'additional_page_attributes_nonce');
echo '<div><label for="additional-page-attributes-checkbox"><input type="checkbox" name="additional-page-attributes-checkbox" id="additional-page-attributes-checkbox" ',$meta ? ' checked="checked"' : '','/>'.$desc.'</label></div>';
}
/**
* When the post is saved, saves our custom data
*/
function save_additional_page_attributes_postdata( $post_id ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $post_id;
if ( !isset( $_POST['additional_page_attributes_nonce'] ) || !wp_verify_nonce($_POST['additional_page_attributes_nonce'], basename( __FILE__ ) ) )
return $post_id;
if ( !current_user_can( 'edit_page', $post_id ) )
return $post_id;
$meta_box_checkbox_value = "";
if( isset( $_POST['additional-page-attributes-checkbox'] ) )
{
$meta_box_checkbox_value = $_POST['additional-page-attributes-checkbox'];
}
update_post_meta($post_id, "additional-page-attributes-checkbox", $meta_box_checkbox_value);
return $post_id;
}
add_action('save_post', 'save_additional_page_attributes_postdata');
/**
* Making menu item unclickable if we checked our checkbox
*/
class YourTheme_Walker_Nav_Menu extends Walker_Nav_Menu {
/**
* @see Walker::start_el()
* @since 3.0.0
*
* @param string $output
* @param object $item Объект элемента меню, подробнее ниже.
* @param int $depth Уровень вложенности элемента меню.
* @param object $args Параметры функции wp_nav_menu
*/
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
global $wp_query,$post;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
/*
* Генерируем строку с CSS-классами элемента меню
*/
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
// функция join превращает массив в строку
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
/*
* Генерируем ID элемента
*/
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
/*
* Генерируем элемент меню
*/
$output .= $indent . '<li' . $id . $value . $class_names .'>';
// атрибуты элемента, title="", rel="", target="" и href=""
$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';
// ссылка и околоссылочный текст
$item_output = $args->before;
// получаем id страницы, получаем значение нашего чекбокса для этой страницы и если значение чекбокса "on", выводим ссылку без атрибутов
$pageid = get_post_meta( $item->ID, '_menu_item_object_id', true );
$checkbox_value = get_post_meta($pageid, "additional-page-attributes-checkbox", true);
if ($checkbox_value == "on") {
$item_output .= '<a class="unclickable">';
} else {
$item_output .= '<a'. $attributes .'>';
}
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
Первая функция добавляет новый метабокс на страницу редактирования/создания ровнёхонько под "Атрибутами страницы". Вторая функция - функция обратного вызова - выводит разметку нашего метабокса, получает meta запись, создаёт временную защиту и добавляет чекбокс. Третья функция сохраняет meta описание нашей записи. Ну а дальше я взял стандартный Walker_Nav_Menu, ввёл две дополнительные переменные (одна получает id страницы, вторая получает значение чекбокса для этой страницы) и при выводе ссылки с атрибутами добавил условие, которое проверяет включен ли чекбокс, или нет. Если включен, то к ссылке данного пункта меню добавляется класс unclickable (его можно использовать, чтобы сделать курсор дефолтным), а атрибуты ссылки удалены.
2 шаг. Дальше, просто-напросто, изменяем вывод меню, где в аргументах указываем наш walker:
$args = array( 'theme_location' => 'YourLocation', 'walker' => new YourTheme_Walker_Nav_Menu() ); wp_nav_menu( $args );
В общем, пока только такой способ. Если у кого будут мысли, как всё таки сделать такую фишку именно в управлении менюшками, буду признателен, если поделитесь.


В общем, есть вот такой класс.
Только он там недоделанный какой-то, кусок из другого кода как-будто. Поэтому я его доделал, вот тебе готовое решение:
if( is_admin() ){ if( ! class_exists('Walker_Nav_Menu_Edit') ) require_once ABSPATH . 'wp-admin/includes/class-walker-nav-menu-edit.php'; ## Наш новый волкер, который подправляет родителя. ## Вылеживаемся, потому что нет подходящего хука... class Custom_Walker_Nav_Menu_Edit extends Walker_Nav_Menu_Edit { function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) { $item_output = ''; parent::start_el( $item_output, $item, $depth, $args, $id ); $output .= preg_replace( // может со временем отвалиться - надо првоерять... '/(?=<(fieldset|p)[^>]+class="[^"]*field-move)/', Menu_Item_Custom_Fields_Example::_fields( $item->ID, $item, $depth, $args ), $item_output ); } } /** * Sample menu item metadata * * This class demonstrate the usage of Menu Item Custom Fields in plugins/themes. * * @since 0.1.0 */ class Menu_Item_Custom_Fields_Example { /** * Holds our custom fields * * @var array * @access protected * @since Menu_Item_Custom_Fields_Example 0.2.0 */ protected static $fields = array( 'field_1' => 'My Field 1', 'field_2' => 'My Field 2', ); /** * Initialize plugin */ public static function init() { add_action( 'wp_update_nav_menu_item', array( __CLASS__, '_save' ), 10, 3 ); add_filter( 'manage_nav-menus_columns', array( __CLASS__, '_columns' ), 99 ); ## заменим базовый волкер add_filter( 'wp_edit_nav_menu_walker', function(){ return 'Custom_Walker_Nav_Menu_Edit'; }, 99 ); } /** * Save custom field value * * @wp_hook action wp_update_nav_menu_item * * @param int $menu_id Nav menu ID * @param int $menu_item_db_id Menu item ID * @param array $menu_item_args Menu item data */ public static function _save( $menu_id, $menu_item_db_id, $menu_item_args ) { if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { return; } check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' ); foreach ( self::$fields as $_key => $label ) { $key = sprintf( 'menu-item-%s', $_key ); // Sanitize if ( ! empty( $_POST[ $key ][ $menu_item_db_id ] ) ) { // Do some checks here... $value = $_POST[ $key ][ $menu_item_db_id ]; } else { $value = null; } // Update if ( ! is_null( $value ) ) { update_post_meta( $menu_item_db_id, $key, $value ); } else { delete_post_meta( $menu_item_db_id, $key ); } } } /** * Print field * * @param object $item Menu item data object. * @param int $depth Depth of menu item. Used for padding. * @param array $args Menu item args. * @param int $id Nav menu ID. * * @return string Form fields */ public static function _fields( $id, $item, $depth, $args ) { ob_start(); foreach ( self::$fields as $_key => $label ){ $key = sprintf( 'menu-item-%s', $_key ); $id = sprintf( 'edit-%s-%s', $key, $item->ID ); $name = sprintf( '%s[%s]', $key, $item->ID ); $value = get_post_meta( $item->ID, $key, true ); $class = sprintf( 'field-%s', $_key ); ?> <p class="description description-wide <?php echo esc_attr( $class ) ?>"> <?php printf( '<label for="%1$s">%2$s<br /><input type="text" id="%1$s" class="widefat %1$s" name="%3$s" value="%4$s" /></label>', esc_attr( $id ), esc_html( $label ), esc_attr( $name ), esc_attr( $value ) ) ?> </p> <?php } return ob_get_clean(); } /** * Add our fields to the screen options toggle * * @param array $columns Menu item columns * @return array */ public static function _columns( $columns ) { $columns = array_merge( $columns, self::$fields ); return $columns; } } Menu_Item_Custom_Fields_Example::init(); }Получим:
Значения храняться в метаполях с ключами:
'menu-item-(key)'. Т.е. где-то во фронте получаешь так:Дальше ты уже сам описал, как через «nav_menu волкер» можно использовать эти значения. Тебе только поле чекбокса нужно вывести и заюзать его на фронте.
Спасибо огромное! Всё сделал, всё работает.
Я себе еще добавил в класс геттер
/** Возвращаем ключи полей * @return array */ public static function getKeyFields(): array { if (empty(self::$fields)) { return []; } return array_keys(self::$fields); }а за пределами
if (is_admin()) {
добавил вот такой фильтр
add_filter('wp_setup_nav_menu_item', function ($menu_item) { $key_fields = MenuItemExpandFields::getKeyFields(); foreach ($key_fields as $key_name) { $value = get_post_meta($menu_item->ID, ('menu-item-' . $key_name), true); if (!empty($value)) { if (!isset($menu_item->expand_field)) { $menu_item->expand_field = []; } $menu_item->expand_field[$key_name] = $value; } } return $menu_item; });который добавляет в поле expand_field в виде массива значений, если они конечно есть
Отличное дополнение