OK, it took me a little while to debug this behaviour, but... Everything is clear for me right now... I'm not sure if this is fully intentional...
Why is it so?
So... The line breaks are stored in DB correctly. Then in wp_setup_nav_menu_item
the description field is filled based on post_content
column of given menu item post.
On line 845 (https://core.trac.wordpress.org/browser/tags/4.9/src/wp-includes/nav-menu.php#L845) it is shortened and filtered:
if ( ! isset( $menu_item->description ) ) {
/**
* Filters a navigation menu item's description.
*
* @since 3.0.0
*
* @param string $description The menu item description.
*/
$menu_item->description = apply_filters( 'nav_menu_description', wp_trim_words( $menu_item->post_content, 200 ) );
}
And the wp_trim_words
function trims text to a certain number of words, but it doesn't preserve line breaks - so any line break is replaced with space.
So what can we do about it?
There are no filters in wp_trim_words
that will allow us to modify its behaviour. There is also no filters that will allow us to skip wp_trim_words
call, but...
You can replace the \n
with some placeholder and then use nav_menu_description
filter to replace it back. There even is no need that users put this placeholder himself - we can replace them with a placeholder with save_post
filter:
function replace_nl_placeholder_nav_menu_description( $description ) {
return str_replace('%NL% ', "\n", $description);
}
add_filter('nav_menu_description', 'replace_nl_placeholder_nav_menu_description');
function replace_nl_with_placeholder_nav_menu_description( $post_id ) {
if ( 'nav_menu_item' !== get_post_type($post_id) ) return;
remove_action( 'save_post', 'replace_nl_with_placeholder_nav_menu_description' );
wp_update_post( array(
'ID' => $post_id,
'post_content' => str_replace("\n", '%NL% ', get_post_field('post_content', $post_id, 'raw'))
) );
add_action( 'save_post', 'replace_nl_with_placeholder_nav_menu_description' );
}
add_action( 'save_post', 'replace_nl_with_placeholder_nav_menu_description' );
And that's all. You only have to remember to properly display such line breaks in your theme (nl2br
and so on).
esc_html()
but in my testing I can't getesc_html()
to remove line breaks, so I don't know why they're removed here.