[Решено/2 способа] Добавление своего пункта (метабокса) в расширенные свойства меню

На странице редактирования меню и управления областями меню в настройках экрана можно включить показ расширенных свойств меню (Цель ссылки, Атрибут title, Классы CSS, Отношение к ссылке (XFN) и Описание) - рис1.

Как добавить к этим 5-ти пунктам, свой 6-ой пункт, допустим "Активность", чтобы в существующем/добавляемом пункте меню появился чекбокс (как в случае с "Цель ссылки" появляется чекбокс "Открывать в новой вкладке"), допустим "Сделать пункт меню неактивным". И если данный чекбокс будет задействован, то у данного пункта меню автоматически уберётся ссылка - рис2.

Меню выводится стандартной функцией wp_nav_menu().

Заметки к вопросу:
OLD_Grays 6.6 лет назад

Пока отделался костылём. В свойствах пункта меню, присваиваю класс 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' );
OLD_Grays 6.5 лет назад

Пока не разобрался ещё как сделать так, как хочется. Сделал немного по-другому. Может кому будет полезно и интересно. Решил зайти с другой стороны.

Альтернативный метод

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 );

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

2
OLD_Grays
6.6 лет назад 44
  • 3
    Kama 9788

    В общем, есть вот такой класс.

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

    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)'. Т.е. где-то во фронте получаешь так:

    get_post_meta( $menu_item_id, 'menu-item-field_1', 1 );
    get_post_meta( $menu_item_id, 'menu-item-field_2', 1 );

    Дальше ты уже сам описал, как через «nav_menu волкер» можно использовать эти значения. Тебе только поле чекбокса нужно вывести и заюзать его на фронте.

    OLD_Grays 6.5 лет назад

    Спасибо огромное! Всё сделал, всё работает.

    YmNIK13 5.1 год назад

    Я себе еще добавил в класс геттер

    /** Возвращаем ключи полей
     * @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 в виде массива значений, если они конечно есть

    OLD_Grays 5.1 год назад

    Отличное дополнение

    Комментировать
На вопросы могут отвечать только зарегистрированные пользователи. Вход . Регистрация