403 lines
13 KiB
PHP
403 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* WP REST API Menu routes
|
|
*
|
|
* @package WP_API_Menus
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit; // Exit if accessed directly
|
|
}
|
|
|
|
if ( ! class_exists( 'WP_REST_Menus' ) ) :
|
|
|
|
|
|
/**
|
|
* WP REST Menus class.
|
|
*
|
|
* WP API Menus support for WP API v2.
|
|
*
|
|
* @package WP_API_Menus
|
|
* @since 1.2.0
|
|
*/
|
|
class WP_REST_Menus {
|
|
|
|
|
|
/**
|
|
* Get WP API namespace.
|
|
*
|
|
* @since 1.2.0
|
|
* @return string
|
|
*/
|
|
public static function get_api_namespace() {
|
|
return 'wp/v2';
|
|
}
|
|
|
|
|
|
/**
|
|
* Get WP API Menus namespace.
|
|
*
|
|
* @since 1.2.1
|
|
* @return string
|
|
*/
|
|
public static function get_plugin_namespace() {
|
|
return 'wp-api-menus/v2';
|
|
}
|
|
|
|
|
|
/**
|
|
* Register menu routes for WP API v2.
|
|
*
|
|
* @since 1.2.0
|
|
*/
|
|
public function register_routes() {
|
|
|
|
register_rest_route( self::get_plugin_namespace(), '/menus', array(
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_menus' ),
|
|
)
|
|
) );
|
|
|
|
register_rest_route( self::get_plugin_namespace(), '/menus/(?P<id>\d+)', array(
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_menu' ),
|
|
'args' => array(
|
|
'context' => array(
|
|
'default' => 'view',
|
|
),
|
|
),
|
|
)
|
|
) );
|
|
|
|
register_rest_route( self::get_plugin_namespace(), '/menu-locations', array(
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_menu_locations' ),
|
|
)
|
|
) );
|
|
|
|
register_rest_route( self::get_plugin_namespace(), '/menu-locations/(?P<location>[a-zA-Z0-9_-]+)', array(
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_menu_location' ),
|
|
)
|
|
) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get menus.
|
|
*
|
|
* @since 1.2.0
|
|
* @return array All registered menus
|
|
*/
|
|
public static function get_menus() {
|
|
|
|
$rest_url = trailingslashit( get_rest_url() . self::get_plugin_namespace() . '/menus/' );
|
|
$wp_menus = wp_get_nav_menus();
|
|
|
|
$i = 0;
|
|
$rest_menus = array();
|
|
foreach ( $wp_menus as $wp_menu ) :
|
|
|
|
$menu = (array) $wp_menu;
|
|
|
|
$rest_menus[ $i ] = $menu;
|
|
$rest_menus[ $i ]['ID'] = $menu['term_id'];
|
|
$rest_menus[ $i ]['name'] = $menu['name'];
|
|
$rest_menus[ $i ]['slug'] = $menu['slug'];
|
|
$rest_menus[ $i ]['description'] = $menu['description'];
|
|
$rest_menus[ $i ]['count'] = $menu['count'];
|
|
|
|
$rest_menus[ $i ]['meta']['links']['collection'] = $rest_url;
|
|
$rest_menus[ $i ]['meta']['links']['self'] = $rest_url . $menu['term_id'];
|
|
|
|
$i ++;
|
|
endforeach;
|
|
|
|
return apply_filters( 'rest_menus_format_menus', $rest_menus );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a menu.
|
|
*
|
|
* @since 1.2.0
|
|
* @param $request
|
|
* @return array Menu data
|
|
*/
|
|
public function get_menu( $request ) {
|
|
|
|
$id = (int) $request['id'];
|
|
$rest_url = get_rest_url() . self::get_api_namespace() . '/menus/';
|
|
$wp_menu_object = $id ? wp_get_nav_menu_object( $id ) : array();
|
|
$wp_menu_items = $id ? wp_get_nav_menu_items( $id ) : array();
|
|
|
|
$rest_menu = array();
|
|
|
|
if ( $wp_menu_object ) :
|
|
|
|
$menu = (array) $wp_menu_object;
|
|
$rest_menu['ID'] = abs( $menu['term_id'] );
|
|
$rest_menu['name'] = $menu['name'];
|
|
$rest_menu['slug'] = $menu['slug'];
|
|
$rest_menu['description'] = $menu['description'];
|
|
$rest_menu['count'] = abs( $menu['count'] );
|
|
|
|
$rest_menu_items = array();
|
|
foreach ( $wp_menu_items as $item_object ) {
|
|
$rest_menu_items[] = $this->format_menu_item( $item_object );
|
|
}
|
|
|
|
$rest_menu_items = $this->nested_menu_items($rest_menu_items, 0);
|
|
|
|
$rest_menu['items'] = $rest_menu_items;
|
|
$rest_menu['meta']['links']['collection'] = $rest_url;
|
|
$rest_menu['meta']['links']['self'] = $rest_url . $id;
|
|
|
|
endif;
|
|
|
|
return apply_filters( 'rest_menus_format_menu', $rest_menu );
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle nested menu items.
|
|
*
|
|
* Given a flat array of menu items, split them into parent/child items
|
|
* and recurse over them to return children nested in their parent.
|
|
*
|
|
* @since 1.2.0
|
|
* @param $menu_items
|
|
* @param $parent
|
|
* @return array
|
|
*/
|
|
private function nested_menu_items( &$menu_items, $parent = null ) {
|
|
|
|
$parents = array();
|
|
$children = array();
|
|
|
|
// Separate menu_items into parents & children.
|
|
array_map( function( $i ) use ( $parent, &$children, &$parents ){
|
|
if ( $i['id'] != $parent && $i['parent'] == $parent ) {
|
|
$parents[] = $i;
|
|
} else {
|
|
$children[] = $i;
|
|
}
|
|
}, $menu_items );
|
|
|
|
foreach ( $parents as &$parent ) {
|
|
|
|
if ( $this->has_children( $children, $parent['id'] ) ) {
|
|
$parent['children'] = $this->nested_menu_items( $children, $parent['id'] );
|
|
}
|
|
}
|
|
|
|
return $parents;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check if a collection of menu items contains an item that is the parent id of 'id'.
|
|
*
|
|
* @since 1.2.0
|
|
* @param array $items
|
|
* @param int $id
|
|
* @return array
|
|
*/
|
|
private function has_children( $items, $id ) {
|
|
return array_filter( $items, function( $i ) use ( $id ) {
|
|
return $i['parent'] == $id;
|
|
} );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get menu locations.
|
|
*
|
|
* @since 1.2.0
|
|
* @param $request
|
|
* @return array All registered menus locations
|
|
*/
|
|
public static function get_menu_locations( $request ) {
|
|
|
|
$locations = get_nav_menu_locations();
|
|
$registered_menus = get_registered_nav_menus();
|
|
$rest_url = get_rest_url() . self::get_api_namespace() . '/menu-locations/';
|
|
$rest_menus = array();
|
|
|
|
if ( $locations && $registered_menus ) :
|
|
|
|
foreach ( $registered_menus as $slug => $label ) :
|
|
|
|
// Sanity check
|
|
if ( ! isset( $locations[ $slug ] ) ) {
|
|
continue;
|
|
}
|
|
|
|
$rest_menus[ $slug ]['ID'] = $locations[ $slug ];
|
|
$rest_menus[ $slug ]['label'] = $label;
|
|
$rest_menus[ $slug ]['meta']['links']['collection'] = $rest_url;
|
|
$rest_menus[ $slug ]['meta']['links']['self'] = $rest_url . $slug;
|
|
|
|
endforeach;
|
|
|
|
endif;
|
|
|
|
return $rest_menus;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get menu for location.
|
|
*
|
|
* @since 1.2.0
|
|
* @param $request
|
|
* @return array The menu for the corresponding location
|
|
*/
|
|
public function get_menu_location( $request ) {
|
|
|
|
$params = $request->get_params();
|
|
$location = $params['location'];
|
|
$locations = get_nav_menu_locations();
|
|
|
|
if ( ! isset( $locations[ $location ] ) ) {
|
|
return array();
|
|
}
|
|
|
|
$wp_menu = wp_get_nav_menu_object( $locations[ $location ] );
|
|
$menu_items = wp_get_nav_menu_items( $wp_menu->term_id );
|
|
|
|
/**
|
|
* wp_get_nav_menu_items() outputs a list that's already sequenced correctly.
|
|
* So the easiest thing to do is to reverse the list and then build our tree
|
|
* from the ground up
|
|
*/
|
|
$rev_items = array_reverse ( $menu_items );
|
|
$rev_menu = array();
|
|
$cache = array();
|
|
|
|
foreach ( $rev_items as $item ) :
|
|
|
|
$formatted = array(
|
|
'ID' => abs( $item->ID ),
|
|
'order' => (int) $item->menu_order,
|
|
'parent' => abs( $item->menu_item_parent ),
|
|
'title' => $item->title,
|
|
'url' => $item->url,
|
|
'attr' => $item->attr_title,
|
|
'target' => $item->target,
|
|
'classes' => implode( ' ', $item->classes ),
|
|
'xfn' => $item->xfn,
|
|
'description' => $item->description,
|
|
'object_id' => abs( $item->object_id ),
|
|
'object' => $item->object,
|
|
'type' => $item->type,
|
|
'type_label' => $item->type_label,
|
|
'children' => array(),
|
|
);
|
|
|
|
if ( array_key_exists( $item->ID , $cache ) ) {
|
|
$formatted['children'] = array_reverse( $cache[ $item->ID ] );
|
|
}
|
|
|
|
$formatted = apply_filters( 'rest_menus_format_menu_item', $formatted );
|
|
|
|
if ( $item->menu_item_parent != 0 ) {
|
|
|
|
if ( array_key_exists( $item->menu_item_parent , $cache ) ) {
|
|
array_push( $cache[ $item->menu_item_parent ], $formatted );
|
|
} else {
|
|
$cache[ $item->menu_item_parent ] = array( $formatted, );
|
|
}
|
|
|
|
} else {
|
|
|
|
array_push( $rev_menu, $formatted );
|
|
}
|
|
|
|
endforeach;
|
|
|
|
return array_reverse ( $rev_menu );
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns all child nav_menu_items under a specific parent.
|
|
*
|
|
* @since 1.2.0
|
|
* @param int $parent_id The parent nav_menu_item ID
|
|
* @param array $nav_menu_items Navigation menu items
|
|
* @param bool $depth Gives all children or direct children only
|
|
* @return array returns filtered array of nav_menu_items
|
|
*/
|
|
public function get_nav_menu_item_children( $parent_id, $nav_menu_items, $depth = true ) {
|
|
|
|
$nav_menu_item_list = array();
|
|
|
|
foreach ( (array) $nav_menu_items as $nav_menu_item ) :
|
|
|
|
if ( $nav_menu_item->menu_item_parent == $parent_id ) :
|
|
|
|
$nav_menu_item_list[] = $this->format_menu_item( $nav_menu_item, true, $nav_menu_items );
|
|
|
|
if ( $depth ) {
|
|
if ( $children = $this->get_nav_menu_item_children( $nav_menu_item->ID, $nav_menu_items ) ) {
|
|
$nav_menu_item_list = array_merge( $nav_menu_item_list, $children );
|
|
}
|
|
}
|
|
|
|
endif;
|
|
|
|
endforeach;
|
|
|
|
return $nav_menu_item_list;
|
|
}
|
|
|
|
|
|
/**
|
|
* Format a menu item for REST API consumption.
|
|
*
|
|
* @since 1.2.0
|
|
* @param object|array $menu_item The menu item
|
|
* @param bool $children Get menu item children (default false)
|
|
* @param array $menu The menu the item belongs to (used when $children is set to true)
|
|
* @return array a formatted menu item for REST
|
|
*/
|
|
public function format_menu_item( $menu_item, $children = false, $menu = array() ) {
|
|
|
|
$item = (array) $menu_item;
|
|
|
|
$menu_item = array(
|
|
'id' => abs( $item['ID'] ),
|
|
'order' => (int) $item['menu_order'],
|
|
'parent' => abs( $item['menu_item_parent'] ),
|
|
'title' => $item['title'],
|
|
'url' => $item['url'],
|
|
'attr' => $item['attr_title'],
|
|
'target' => $item['target'],
|
|
'classes' => implode( ' ', $item['classes'] ),
|
|
'xfn' => $item['xfn'],
|
|
'description' => $item['description'],
|
|
'object_id' => abs( $item['object_id'] ),
|
|
'object' => $item['object'],
|
|
'object_slug' => get_post( $item['object_id'] )->post_name,
|
|
'type' => $item['type'],
|
|
'type_label' => $item['type_label'],
|
|
);
|
|
|
|
if ( $children === true && ! empty( $menu ) ) {
|
|
$menu_item['children'] = $this->get_nav_menu_item_children( $item['ID'], $menu );
|
|
}
|
|
|
|
return apply_filters( 'rest_menus_format_menu_item', $menu_item );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
endif;
|