foreach ( $term_order as $term_id ) {
if ( isset( $terms_by_id[ "$term_id" ] ) ) {
$return[] = $terms_by_id[ "$term_id" ];
unset( $terms_by_id[ "$term_id" ] );
}
}
foreach ( $terms_by_id as $term_id => $term ) {
$return[] = $term;
}
return $return;
}
/**
* Get first menu taxonomy "leaf".
*
* @param int $post_id Post ID.
*
* @return bool|\WP_Term|\WP_Error|null
*/
public function get_menu_item_menu_leaf( $post_id ) {
// Get first menu taxonomy "leaf".
$term_ids = wp_get_object_terms( $post_id, self::MENU_TAX, array( 'fields' => 'ids' ) );
foreach ( $term_ids as $term_id ) { // possibly ignore PhanTypeSuspiciousNonTraversableForeach
$children = get_term_children( $term_id, self::MENU_TAX );
if ( ! $children ) {
break;
}
}
if ( ! isset( $term_id ) ) {
return false;
}
return get_term( $term_id, self::MENU_TAX );
}
/**
* Get a list of the labels linked to a menu item.
*
* @param int $post_id Post ID.
*
* @return void
*/
public function list_labels( $post_id = 0 ) {
$post = get_post( $post_id );
echo get_the_term_list( $post->ID, self::MENU_ITEM_LABEL_TAX, '', _x( ', ', 'Nova label separator', 'jetpack-classic-theme-helper' ), '' );
}
/**
* Get a list of the labels linked to a menu item, with links to manage them.
*
* @param int $post_id Post ID.
*
* @return void
*/
public function list_admin_labels( $post_id = 0 ) {
$post = get_post( $post_id );
$labels = get_the_terms( $post->ID, self::MENU_ITEM_LABEL_TAX );
if ( ! empty( $labels ) ) {
$out = array();
foreach ( $labels as $label ) { // possibly ignore PhanTypeSuspiciousNonTraversableForeach
$out[] = sprintf(
'%s',
esc_url(
add_query_arg(
array(
'post_type' => self::MENU_ITEM_POST_TYPE,
'taxonomy' => self::MENU_ITEM_LABEL_TAX,
'term' => $label->slug,
),
'edit.php'
)
),
esc_html(
sanitize_term_field( 'name', $label->name, $label->term_id, self::MENU_ITEM_LABEL_TAX, 'display' )
)
);
}
echo implode( _x( ', ', 'Nova label separator', 'jetpack-classic-theme-helper' ), $out ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- we build $out ourselves and escape things there.
} else {
esc_html_e( 'No Labels', 'jetpack-classic-theme-helper' );
}
}
/**
* Update post meta with the price defined in meta box.
*
* @param int $post_id Post ID.
* @param string $price Price.
*
* @return int|bool
*/
public function set_price( $post_id = 0, $price = '' ) {
return update_post_meta( $post_id, 'nova_price', $price );
}
/**
* Get the price of a menu item.
*
* @param int $post_id Post ID.
*
* @return bool|string
*/
public function get_price( $post_id = 0 ) {
return get_post_meta( $post_id, 'nova_price', true );
}
/**
* Echo the price of a menu item.
*
* @param int $post_id Post ID.
*
* @return void
*/
public function display_price( $post_id = 0 ) {
echo esc_html( $this->get_price( $post_id ) );
}
/* Menu Item Loop Markup */
/**
* Get markup for a menu item.
* Note: Does not support nested loops.
*
* @param null|string $field The field to get the value for.
*
* @return array
*/
public function get_menu_item_loop_markup( $field = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
return $this->menu_item_loop_markup;
}
/**
* Sets up the loop markup.
* Attached to the 'template_include' *filter*,
* which fires only during a real blog view (not in admin, feeds, etc.)
*
* @param string $template Template File.
*
* @return string Template File. VERY Important.
*/
public function setup_menu_item_loop_markup__in_filter( $template ) {
add_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
return $template;
}
/**
* If the Query is a Menu Item Query, start outputing the Menu Item Loop Marku
* Attached to the 'loop_start' action.
*
* @param WP_Query $query Post query.
*
* @return void
*/
public function start_menu_item_loop( $query ) {
if ( ! $this->is_menu_item_query( $query ) ) {
return;
}
$this->menu_item_loop_last_term_id = false;
$this->menu_item_loop_current_term = false;
add_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
add_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
}
/**
* Outputs the Menu Item Loop Markup
* Attached to the 'the_post' action.
*
* @param WP_Post $post Post object.
*
* @return void
*/
public function menu_item_loop_each_post( $post ) {
$this->menu_item_loop_current_term = $this->get_menu_item_menu_leaf( $post->ID );
if (
false === $this->menu_item_loop_current_term
|| null === $this->menu_item_loop_current_term
|| is_wp_error( $this->menu_item_loop_current_term )
) {
return;
}
if ( false === $this->menu_item_loop_last_term_id ) {
// We're at the very beginning of the loop
$this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
$this->menu_item_loop_header(); // Output the menu's header
} elseif (
is_object( $this->menu_item_loop_current_term ) &&
$this->menu_item_loop_last_term_id !== $this->menu_item_loop_current_term->term_id
) {
// We're not at the very beginning but still need to start a new menu section. End the previous menu section first.
$this->menu_item_loop_close_element( 'menu' ); // End the previous menu section
$this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
$this->menu_item_loop_header(); // Output the menu's header
}
if ( is_object( $this->menu_item_loop_current_term ) ) {
$this->menu_item_loop_last_term_id = $this->menu_item_loop_current_term->term_id;
}
}
/**
* If the Query is a Menu Item Query, stop outputing the Menu Item Loop Marku
* Attached to the 'loop_end' action.
*
* @param WP_Query $query Post query.
*
* @return void
*/
public function stop_menu_item_loop( $query ) {
if ( ! $this->is_menu_item_query( $query ) ) {
return;
}
remove_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
remove_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
remove_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
$this->menu_item_loop_close_element( 'menu' ); // End the last menu section
}
/**
* Outputs the Menu Group Header
*
* @return void
*/
public function menu_item_loop_header() {
$this->menu_item_loop_open_element( 'menu_header' );
$this->menu_item_loop_open_element( 'menu_title' );
echo esc_html( $this->menu_item_loop_current_term->name ); // @todo tax filter
$this->menu_item_loop_close_element( 'menu_title' );
if ( $this->menu_item_loop_current_term->description ) :
$this->menu_item_loop_open_element( 'menu_description' );
echo esc_html( $this->menu_item_loop_current_term->description ); // @todo kses, tax filter
$this->menu_item_loop_close_element( 'menu_description' );
endif;
$this->menu_item_loop_close_element( 'menu_header' );
}
/**
* Outputs a Menu Item Markup element opening tag
*
* @param string $field - Menu Item Markup settings field.
*
* @return void
*/
public function menu_item_loop_open_element( $field ) {
$markup = $this->get_menu_item_loop_markup();
/**
* Filter a menu item's element opening tag.
*
* @module custom-content-types
*
* @since 4.4.0
*
* @param string $tag Menu item's element opening tag.
* @param string $field Menu Item Markup settings field.
* @param array $markup Array of markup elements for the menu item.
* @param false|object $term Taxonomy term for current menu item.
*/
echo apply_filters( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- it's escaped in menu_item_loop_class.
'jetpack_nova_menu_item_loop_open_element',
'<' . tag_escape( $markup[ "{$field}_tag" ] ) . $this->menu_item_loop_class( $markup[ "{$field}_class" ] ) . ">\n",
$field,
$markup,
$this->menu_item_loop_current_term
);
}
/**
* Outputs a Menu Item Markup element closing tag
*
* @param string $field - Menu Item Markup settings field.
*
* @return void
*/
public function menu_item_loop_close_element( $field ) {
$markup = $this->get_menu_item_loop_markup();
/**
* Filter a menu item's element closing tag.
*
* @module custom-content-types
*
* @since 4.4.0
*
* @param string $tag Menu item's element closing tag.
* @param string $field Menu Item Markup settings field.
* @param array $markup Array of markup elements for the menu item.
* @param false|object $term Taxonomy term for current menu item.
*/
echo apply_filters( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- tag_escape is used.
'jetpack_nova_menu_item_loop_close_element',
'' . tag_escape( $markup[ "{$field}_tag" ] ) . ">\n",
$field,
$markup,
$this->menu_item_loop_current_term
);
}
/**
* Returns a Menu Item Markup element's class attribute.
*
* @param string $class Class name.
*
* @return string HTML class attribute with leading whitespace.
*/
public function menu_item_loop_class( $class ) {
if ( ! $class ) {
return '';
}
/**
* Filter a menu Item Markup element's class attribute.
*
* @module custom-content-types
*
* @since 4.4.0
*
* @param string $tag Menu Item Markup element's class attribute.
* @param string $class Menu Item Class name.
* @param false|object $term Taxonomy term for current menu item.
*/
return apply_filters(
'jetpack_nova_menu_item_loop_class',
' class="' . esc_attr( $class ) . '"',
$class,
$this->menu_item_loop_current_term
);
}
}
}
foreach ( $term_order as $term_id ) {
if ( isset( $terms_by_id[ "$term_id" ] ) ) {
$return[] = $terms_by_id[ "$term_id" ];
unset( $terms_by_id[ "$term_id" ] );
}
}
foreach ( $terms_by_id as $term_id => $term ) {
$return[] = $term;
}
return $return;
}
/**
* Get first menu taxonomy "leaf".
*
* @param int $post_id Post ID.
*
* @return bool|\WP_Term|\WP_Error|null
*/
public function get_menu_item_menu_leaf( $post_id ) {
// Get first menu taxonomy "leaf".
$term_ids = wp_get_object_terms( $post_id, self::MENU_TAX, array( 'fields' => 'ids' ) );
foreach ( $term_ids as $term_id ) { // possibly ignore PhanTypeSuspiciousNonTraversableForeach
$children = get_term_children( $term_id, self::MENU_TAX );
if ( ! $children ) {
break;
}
}
if ( ! isset( $term_id ) ) {
return false;
}
return get_term( $term_id, self::MENU_TAX );
}
/**
* Get a list of the labels linked to a menu item.
*
* @param int $post_id Post ID.
*
* @return void
*/
public function list_labels( $post_id = 0 ) {
$post = get_post( $post_id );
echo get_the_term_list( $post->ID, self::MENU_ITEM_LABEL_TAX, '', _x( ', ', 'Nova label separator', 'jetpack-classic-theme-helper' ), '' );
}
/**
* Get a list of the labels linked to a menu item, with links to manage them.
*
* @param int $post_id Post ID.
*
* @return void
*/
public function list_admin_labels( $post_id = 0 ) {
$post = get_post( $post_id );
$labels = get_the_terms( $post->ID, self::MENU_ITEM_LABEL_TAX );
if ( ! empty( $labels ) ) {
$out = array();
foreach ( $labels as $label ) { // possibly ignore PhanTypeSuspiciousNonTraversableForeach
$out[] = sprintf(
'%s',
esc_url(
add_query_arg(
array(
'post_type' => self::MENU_ITEM_POST_TYPE,
'taxonomy' => self::MENU_ITEM_LABEL_TAX,
'term' => $label->slug,
),
'edit.php'
)
),
esc_html(
sanitize_term_field( 'name', $label->name, $label->term_id, self::MENU_ITEM_LABEL_TAX, 'display' )
)
);
}
echo implode( _x( ', ', 'Nova label separator', 'jetpack-classic-theme-helper' ), $out ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- we build $out ourselves and escape things there.
} else {
esc_html_e( 'No Labels', 'jetpack-classic-theme-helper' );
}
}
/**
* Update post meta with the price defined in meta box.
*
* @param int $post_id Post ID.
* @param string $price Price.
*
* @return int|bool
*/
public function set_price( $post_id = 0, $price = '' ) {
return update_post_meta( $post_id, 'nova_price', $price );
}
/**
* Get the price of a menu item.
*
* @param int $post_id Post ID.
*
* @return bool|string
*/
public function get_price( $post_id = 0 ) {
return get_post_meta( $post_id, 'nova_price', true );
}
/**
* Echo the price of a menu item.
*
* @param int $post_id Post ID.
*
* @return void
*/
public function display_price( $post_id = 0 ) {
echo esc_html( $this->get_price( $post_id ) );
}
/* Menu Item Loop Markup */
/**
* Get markup for a menu item.
* Note: Does not support nested loops.
*
* @param null|string $field The field to get the value for.
*
* @return array
*/
public function get_menu_item_loop_markup( $field = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
return $this->menu_item_loop_markup;
}
/**
* Sets up the loop markup.
* Attached to the 'template_include' *filter*,
* which fires only during a real blog view (not in admin, feeds, etc.)
*
* @param string $template Template File.
*
* @return string Template File. VERY Important.
*/
public function setup_menu_item_loop_markup__in_filter( $template ) {
add_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
return $template;
}
/**
* If the Query is a Menu Item Query, start outputing the Menu Item Loop Marku
* Attached to the 'loop_start' action.
*
* @param WP_Query $query Post query.
*
* @return void
*/
public function start_menu_item_loop( $query ) {
if ( ! $this->is_menu_item_query( $query ) ) {
return;
}
$this->menu_item_loop_last_term_id = false;
$this->menu_item_loop_current_term = false;
add_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
add_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
}
/**
* Outputs the Menu Item Loop Markup
* Attached to the 'the_post' action.
*
* @param WP_Post $post Post object.
*
* @return void
*/
public function menu_item_loop_each_post( $post ) {
$this->menu_item_loop_current_term = $this->get_menu_item_menu_leaf( $post->ID );
if (
false === $this->menu_item_loop_current_term
|| null === $this->menu_item_loop_current_term
|| is_wp_error( $this->menu_item_loop_current_term )
) {
return;
}
if ( false === $this->menu_item_loop_last_term_id ) {
// We're at the very beginning of the loop
$this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
$this->menu_item_loop_header(); // Output the menu's header
} elseif (
is_object( $this->menu_item_loop_current_term ) &&
$this->menu_item_loop_last_term_id !== $this->menu_item_loop_current_term->term_id
) {
// We're not at the very beginning but still need to start a new menu section. End the previous menu section first.
$this->menu_item_loop_close_element( 'menu' ); // End the previous menu section
$this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
$this->menu_item_loop_header(); // Output the menu's header
}
if ( is_object( $this->menu_item_loop_current_term ) ) {
$this->menu_item_loop_last_term_id = $this->menu_item_loop_current_term->term_id;
}
}
/**
* If the Query is a Menu Item Query, stop outputing the Menu Item Loop Marku
* Attached to the 'loop_end' action.
*
* @param WP_Query $query Post query.
*
* @return void
*/
public function stop_menu_item_loop( $query ) {
if ( ! $this->is_menu_item_query( $query ) ) {
return;
}
remove_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
remove_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
remove_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
$this->menu_item_loop_close_element( 'menu' ); // End the last menu section
}
/**
* Outputs the Menu Group Header
*
* @return void
*/
public function menu_item_loop_header() {
$this->menu_item_loop_open_element( 'menu_header' );
$this->menu_item_loop_open_element( 'menu_title' );
echo esc_html( $this->menu_item_loop_current_term->name ); // @todo tax filter
$this->menu_item_loop_close_element( 'menu_title' );
if ( $this->menu_item_loop_current_term->description ) :
$this->menu_item_loop_open_element( 'menu_description' );
echo esc_html( $this->menu_item_loop_current_term->description ); // @todo kses, tax filter
$this->menu_item_loop_close_element( 'menu_description' );
endif;
$this->menu_item_loop_close_element( 'menu_header' );
}
/**
* Outputs a Menu Item Markup element opening tag
*
* @param string $field - Menu Item Markup settings field.
*
* @return void
*/
public function menu_item_loop_open_element( $field ) {
$markup = $this->get_menu_item_loop_markup();
/**
* Filter a menu item's element opening tag.
*
* @module custom-content-types
*
* @since 4.4.0
*
* @param string $tag Menu item's element opening tag.
* @param string $field Menu Item Markup settings field.
* @param array $markup Array of markup elements for the menu item.
* @param false|object $term Taxonomy term for current menu item.
*/
echo apply_filters( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- it's escaped in menu_item_loop_class.
'jetpack_nova_menu_item_loop_open_element',
'<' . tag_escape( $markup[ "{$field}_tag" ] ) . $this->menu_item_loop_class( $markup[ "{$field}_class" ] ) . ">\n",
$field,
$markup,
$this->menu_item_loop_current_term
);
}
/**
* Outputs a Menu Item Markup element closing tag
*
* @param string $field - Menu Item Markup settings field.
*
* @return void
*/
public function menu_item_loop_close_element( $field ) {
$markup = $this->get_menu_item_loop_markup();
/**
* Filter a menu item's element closing tag.
*
* @module custom-content-types
*
* @since 4.4.0
*
* @param string $tag Menu item's element closing tag.
* @param string $field Menu Item Markup settings field.
* @param array $markup Array of markup elements for the menu item.
* @param false|object $term Taxonomy term for current menu item.
*/
echo apply_filters( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- tag_escape is used.
'jetpack_nova_menu_item_loop_close_element',
'' . tag_escape( $markup[ "{$field}_tag" ] ) . ">\n",
$field,
$markup,
$this->menu_item_loop_current_term
);
}
/**
* Returns a Menu Item Markup element's class attribute.
*
* @param string $class Class name.
*
* @return string HTML class attribute with leading whitespace.
*/
public function menu_item_loop_class( $class ) {
if ( ! $class ) {
return '';
}
/**
* Filter a menu Item Markup element's class attribute.
*
* @module custom-content-types
*
* @since 4.4.0
*
* @param string $tag Menu Item Markup element's class attribute.
* @param string $class Menu Item Class name.
* @param false|object $term Taxonomy term for current menu item.
*/
return apply_filters(
'jetpack_nova_menu_item_loop_class',
' class="' . esc_attr( $class ) . '"',
$class,
$this->menu_item_loop_current_term
);
}
}
}