class-wp-rest-menu-locations-controller.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. /**
  3. * REST API: WP_REST_Menu_Locations_Controller class
  4. *
  5. * @package WordPress
  6. * @subpackage REST_API
  7. * @since 5.9.0
  8. */
  9. /**
  10. * Core class used to access menu locations via the REST API.
  11. *
  12. * @since 5.9.0
  13. *
  14. * @see WP_REST_Controller
  15. */
  16. class WP_REST_Menu_Locations_Controller extends WP_REST_Controller {
  17. /**
  18. * Menu Locations Constructor.
  19. *
  20. * @since 5.9.0
  21. */
  22. public function __construct() {
  23. $this->namespace = 'wp/v2';
  24. $this->rest_base = 'menu-locations';
  25. }
  26. /**
  27. * Registers the routes for the objects of the controller.
  28. *
  29. * @since 5.9.0
  30. *
  31. * @see register_rest_route()
  32. */
  33. public function register_routes() {
  34. register_rest_route(
  35. $this->namespace,
  36. '/' . $this->rest_base,
  37. array(
  38. array(
  39. 'methods' => WP_REST_Server::READABLE,
  40. 'callback' => array( $this, 'get_items' ),
  41. 'permission_callback' => array( $this, 'get_items_permissions_check' ),
  42. 'args' => $this->get_collection_params(),
  43. ),
  44. 'schema' => array( $this, 'get_public_item_schema' ),
  45. )
  46. );
  47. register_rest_route(
  48. $this->namespace,
  49. '/' . $this->rest_base . '/(?P<location>[\w-]+)',
  50. array(
  51. 'args' => array(
  52. 'location' => array(
  53. 'description' => __( 'An alphanumeric identifier for the menu location.' ),
  54. 'type' => 'string',
  55. ),
  56. ),
  57. array(
  58. 'methods' => WP_REST_Server::READABLE,
  59. 'callback' => array( $this, 'get_item' ),
  60. 'permission_callback' => array( $this, 'get_item_permissions_check' ),
  61. 'args' => array(
  62. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  63. ),
  64. ),
  65. 'schema' => array( $this, 'get_public_item_schema' ),
  66. )
  67. );
  68. }
  69. /**
  70. * Checks whether a given request has permission to read menu locations.
  71. *
  72. * @since 5.9.0
  73. *
  74. * @param WP_REST_Request $request Full details about the request.
  75. * @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
  76. */
  77. public function get_items_permissions_check( $request ) {
  78. if ( ! current_user_can( 'edit_theme_options' ) ) {
  79. return new WP_Error(
  80. 'rest_cannot_view',
  81. __( 'Sorry, you are not allowed to view menu locations.' ),
  82. array( 'status' => rest_authorization_required_code() )
  83. );
  84. }
  85. return true;
  86. }
  87. /**
  88. * Retrieves all menu locations, depending on user context.
  89. *
  90. * @since 5.9.0
  91. *
  92. * @param WP_REST_Request $request Full details about the request.
  93. * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
  94. */
  95. public function get_items( $request ) {
  96. $data = array();
  97. foreach ( get_registered_nav_menus() as $name => $description ) {
  98. $location = new stdClass();
  99. $location->name = $name;
  100. $location->description = $description;
  101. $location = $this->prepare_item_for_response( $location, $request );
  102. $data[ $name ] = $this->prepare_response_for_collection( $location );
  103. }
  104. return rest_ensure_response( $data );
  105. }
  106. /**
  107. * Checks if a given request has access to read a menu location.
  108. *
  109. * @since 5.9.0
  110. *
  111. * @param WP_REST_Request $request Full details about the request.
  112. * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
  113. */
  114. public function get_item_permissions_check( $request ) {
  115. if ( ! current_user_can( 'edit_theme_options' ) ) {
  116. return new WP_Error(
  117. 'rest_cannot_view',
  118. __( 'Sorry, you are not allowed to view menu locations.' ),
  119. array( 'status' => rest_authorization_required_code() )
  120. );
  121. }
  122. return true;
  123. }
  124. /**
  125. * Retrieves a specific menu location.
  126. *
  127. * @since 5.9.0
  128. *
  129. * @param WP_REST_Request $request Full details about the request.
  130. * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
  131. */
  132. public function get_item( $request ) {
  133. $registered_menus = get_registered_nav_menus();
  134. if ( ! array_key_exists( $request['location'], $registered_menus ) ) {
  135. return new WP_Error( 'rest_menu_location_invalid', __( 'Invalid menu location.' ), array( 'status' => 404 ) );
  136. }
  137. $location = new stdClass();
  138. $location->name = $request['location'];
  139. $location->description = $registered_menus[ $location->name ];
  140. $data = $this->prepare_item_for_response( $location, $request );
  141. return rest_ensure_response( $data );
  142. }
  143. /**
  144. * Prepares a menu location object for serialization.
  145. *
  146. * @since 5.9.0
  147. *
  148. * @param stdClass $item Post status data.
  149. * @param WP_REST_Request $request Full details about the request.
  150. * @return WP_REST_Response Menu location data.
  151. */
  152. public function prepare_item_for_response( $item, $request ) {
  153. // Restores the more descriptive, specific name for use within this method.
  154. $location = $item;
  155. $locations = get_nav_menu_locations();
  156. $menu = isset( $locations[ $location->name ] ) ? $locations[ $location->name ] : 0;
  157. $fields = $this->get_fields_for_response( $request );
  158. $data = array();
  159. if ( rest_is_field_included( 'name', $fields ) ) {
  160. $data['name'] = $location->name;
  161. }
  162. if ( rest_is_field_included( 'description', $fields ) ) {
  163. $data['description'] = $location->description;
  164. }
  165. if ( rest_is_field_included( 'menu', $fields ) ) {
  166. $data['menu'] = (int) $menu;
  167. }
  168. $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  169. $data = $this->add_additional_fields_to_object( $data, $request );
  170. $data = $this->filter_response_by_context( $data, $context );
  171. $response = rest_ensure_response( $data );
  172. if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
  173. $response->add_links( $this->prepare_links( $location ) );
  174. }
  175. /**
  176. * Filters menu location data returned from the REST API.
  177. *
  178. * @since 5.9.0
  179. *
  180. * @param WP_REST_Response $response The response object.
  181. * @param object $location The original location object.
  182. * @param WP_REST_Request $request Request used to generate the response.
  183. */
  184. return apply_filters( 'rest_prepare_menu_location', $response, $location, $request );
  185. }
  186. /**
  187. * Prepares links for the request.
  188. *
  189. * @since 5.9.0
  190. *
  191. * @param stdClass $location Menu location.
  192. * @return array Links for the given menu location.
  193. */
  194. protected function prepare_links( $location ) {
  195. $base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
  196. // Entity meta.
  197. $links = array(
  198. 'self' => array(
  199. 'href' => rest_url( trailingslashit( $base ) . $location->name ),
  200. ),
  201. 'collection' => array(
  202. 'href' => rest_url( $base ),
  203. ),
  204. );
  205. $locations = get_nav_menu_locations();
  206. $menu = isset( $locations[ $location->name ] ) ? $locations[ $location->name ] : 0;
  207. if ( $menu ) {
  208. $path = rest_get_route_for_term( $menu );
  209. if ( $path ) {
  210. $url = rest_url( $path );
  211. $links['https://api.w.org/menu'][] = array(
  212. 'href' => $url,
  213. 'embeddable' => true,
  214. );
  215. }
  216. }
  217. return $links;
  218. }
  219. /**
  220. * Retrieves the menu location's schema, conforming to JSON Schema.
  221. *
  222. * @since 5.9.0
  223. *
  224. * @return array Item schema data.
  225. */
  226. public function get_item_schema() {
  227. if ( $this->schema ) {
  228. return $this->add_additional_fields_schema( $this->schema );
  229. }
  230. $this->schema = array(
  231. '$schema' => 'http://json-schema.org/draft-04/schema#',
  232. 'title' => 'menu-location',
  233. 'type' => 'object',
  234. 'properties' => array(
  235. 'name' => array(
  236. 'description' => __( 'The name of the menu location.' ),
  237. 'type' => 'string',
  238. 'context' => array( 'embed', 'view', 'edit' ),
  239. 'readonly' => true,
  240. ),
  241. 'description' => array(
  242. 'description' => __( 'The description of the menu location.' ),
  243. 'type' => 'string',
  244. 'context' => array( 'embed', 'view', 'edit' ),
  245. 'readonly' => true,
  246. ),
  247. 'menu' => array(
  248. 'description' => __( 'The ID of the assigned menu.' ),
  249. 'type' => 'integer',
  250. 'context' => array( 'embed', 'view', 'edit' ),
  251. 'readonly' => true,
  252. ),
  253. ),
  254. );
  255. return $this->add_additional_fields_schema( $this->schema );
  256. }
  257. /**
  258. * Retrieves the query params for collections.
  259. *
  260. * @since 5.9.0
  261. *
  262. * @return array Collection parameters.
  263. */
  264. public function get_collection_params() {
  265. return array(
  266. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  267. );
  268. }
  269. }