Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | abstract class WPCOM_JSON_API_Menus_Abstract_Endpoint extends WPCOM_JSON_API_Endpoint { |
||
| 3 | |||
| 4 | protected function switch_to_blog_and_validate_user( $site ) { |
||
| 5 | $site_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site ) ); |
||
| 6 | if ( is_wp_error( $site_id ) ) { |
||
| 7 | return $site_id; |
||
| 8 | } |
||
| 9 | |||
| 10 | if ( ! current_user_can( 'edit_theme_options' ) ) { |
||
| 11 | return new WP_Error( 'unauthorised', 'User cannot edit theme options on this site.', 403 ); |
||
|
0 ignored issues
–
show
|
|||
| 12 | } |
||
| 13 | |||
| 14 | if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { |
||
| 15 | $this->load_theme_functions(); |
||
| 16 | } |
||
| 17 | |||
| 18 | return $site_id; |
||
| 19 | } |
||
| 20 | |||
| 21 | |||
| 22 | protected function get_locations() { |
||
| 23 | $locations = array(); |
||
| 24 | $menus = get_registered_nav_menus(); |
||
| 25 | if ( !empty( $menus ) ) { |
||
| 26 | foreach( $menus as $name => $description ) { |
||
| 27 | $locations[] = array( 'name' => $name, 'description' => $description ); |
||
| 28 | } |
||
| 29 | } |
||
| 30 | |||
| 31 | $locations = array_merge( $locations, WPCOM_JSON_API_Menus_Widgets::get() ); |
||
| 32 | |||
| 33 | // Primary (first) location should have defaultState -> default, |
||
| 34 | // all other locations (including widgets) should have defaultState -> empty. |
||
| 35 | for ( $i = 0; $i < count( $locations ); $i++ ) { |
||
| 36 | $locations[ $i ]['defaultState'] = $i ? 'empty' : 'default'; |
||
| 37 | } |
||
| 38 | return $locations; |
||
| 39 | } |
||
| 40 | |||
| 41 | protected function simplify( $data ) { |
||
| 42 | $simplifier = new WPCOM_JSON_API_Menus_Simplifier( $data ); |
||
| 43 | return $simplifier->translate(); |
||
| 44 | } |
||
| 45 | |||
| 46 | protected function complexify( $data ) { |
||
| 47 | $complexifier = new WPCOM_JSON_API_Menus_Complexify( $data ); |
||
| 48 | return $complexifier->translate(); |
||
| 49 | } |
||
| 50 | } |
||
| 51 | |||
| 52 | abstract class WPCOM_JSON_API_Menus_Translator { |
||
| 53 | protected $filter = ''; |
||
| 54 | |||
| 55 | protected $filters = array(); |
||
| 56 | |||
| 57 | public function __construct( $menus ) { |
||
| 58 | $this->is_single_menu = ! is_array( $menus ); |
||
| 59 | $this->menus = is_array( $menus ) ? $menus : array( $menus ); |
||
| 60 | } |
||
| 61 | |||
| 62 | public function translate() { |
||
| 63 | $result = $this->menus; |
||
| 64 | foreach ( $this->filters as $f ) { |
||
| 65 | $result = call_user_func( array( $this, $f ), $result ); |
||
| 66 | if ( is_wp_error($result ) ) { |
||
| 67 | return $result; |
||
| 68 | } |
||
| 69 | } |
||
| 70 | return $this->maybe_extract( $result ); |
||
| 71 | } |
||
| 72 | |||
| 73 | protected function maybe_extract( $menus ) { |
||
| 74 | return $this->is_single_menu ? $menus[0] : $menus; |
||
| 75 | } |
||
| 76 | |||
| 77 | public function whitelist_and_rename_with( $object, $dict ) { |
||
| 78 | $keys = array_keys( $dict ); |
||
| 79 | $return = array(); |
||
| 80 | foreach ( (array) $object as $k => $v ) { |
||
| 81 | if ( in_array( $k, $keys ) ) { |
||
| 82 | if ( is_array( $dict[ $k ] ) ) { |
||
| 83 | settype( $v, $dict[ $k ]['type'] ); |
||
| 84 | $return[ $dict[ $k ]['name'] ] = $v; |
||
| 85 | } else { |
||
| 86 | $new_k = $dict[ $k ]; |
||
| 87 | $return[ $new_k ] = $v; |
||
| 88 | } |
||
| 89 | } |
||
| 90 | } |
||
| 91 | return $return; |
||
| 92 | } |
||
| 93 | } |
||
| 94 | |||
| 95 | class WPCOM_JSON_API_Menus_Simplifier extends WPCOM_JSON_API_Menus_Translator { |
||
| 96 | protected $filter = 'wpcom_menu_api_translator_simplify'; |
||
| 97 | |||
| 98 | protected $filters = array( |
||
| 99 | 'whitelist_and_rename_keys', |
||
| 100 | 'add_locations', |
||
| 101 | 'treeify', |
||
| 102 | 'add_widget_locations', |
||
| 103 | ); |
||
| 104 | |||
| 105 | protected $menu_whitelist = array( |
||
| 106 | 'term_id' => array( 'name' => 'id', 'type' => 'int' ), |
||
| 107 | 'name' => array( 'name' => 'name', 'type' => 'string' ), |
||
| 108 | 'description' => array( 'name' => 'description', 'type' => 'string' ), |
||
| 109 | 'items' => array( 'name' => 'items', 'type' => 'array' ), |
||
| 110 | ); |
||
| 111 | |||
| 112 | protected $menu_item_whitelist = array( |
||
| 113 | 'db_id' => array( 'name' => 'id', 'type' => 'int' ), |
||
| 114 | 'object_id' => array( 'name' => 'content_id', 'type' => 'int' ), |
||
| 115 | 'object' => array( 'name' => 'type', 'type' => 'string' ), |
||
| 116 | 'type' => array( 'name' => 'type_family', 'type' => 'string' ), |
||
| 117 | 'type_label' => array( 'name' => 'type_label', 'type' => 'string' ), |
||
| 118 | 'title' => array( 'name' => 'name', 'type' => 'string' ), |
||
| 119 | 'menu_order' => array( 'name' => 'order', 'type' => 'int' ), |
||
| 120 | 'menu_item_parent' => array( 'name' => 'parent', 'type' => 'int' ), |
||
| 121 | 'url' => array( 'name' => 'url', 'type' => 'string' ), |
||
| 122 | 'target' => array( 'name' => 'link_target', 'type' => 'string' ), |
||
| 123 | 'attr_title' => array( 'name' => 'link_title', 'type' => 'string' ), |
||
| 124 | 'description' => array( 'name' => 'description', 'type' => 'string' ), |
||
| 125 | 'classes' => array( 'name' => 'classes', 'type' => 'array' ), |
||
| 126 | 'xfn' => array( 'name' => 'xfn', 'type' => 'string' ), |
||
| 127 | ); |
||
| 128 | |||
| 129 | /************************** |
||
| 130 | * Filters methods |
||
| 131 | **************************/ |
||
| 132 | |||
| 133 | public function treeify( $menus ) { |
||
| 134 | return array_map( array( $this, 'treeify_menu' ), $menus ); |
||
| 135 | } |
||
| 136 | |||
| 137 | // turn the flat item list into a tree of items |
||
| 138 | protected function treeify_menu( $menu ) { |
||
| 139 | $indexed_nodes = array(); |
||
| 140 | $tree = array(); |
||
| 141 | |||
| 142 | foreach( $menu['items'] as &$item ) { |
||
| 143 | $indexed_nodes[ $item['id'] ] = &$item; |
||
| 144 | } |
||
| 145 | |||
| 146 | foreach( $menu['items'] as &$item ) { |
||
| 147 | if ( $item['parent'] && isset( $indexed_nodes[ $item['parent'] ] ) ) { |
||
| 148 | $parent_node = &$indexed_nodes[ $item['parent'] ]; |
||
| 149 | if ( !isset( $parent_node['items'] ) ) { |
||
| 150 | $parent_node['items'] = array(); |
||
| 151 | } |
||
| 152 | $parent_node['items'][ $item['order'] ] = &$item; |
||
| 153 | } else { |
||
| 154 | $tree[ $item['order'] ] = &$item; |
||
| 155 | } |
||
| 156 | unset( $item['order'] ); |
||
| 157 | unset( $item['parent'] ); |
||
| 158 | } |
||
| 159 | |||
| 160 | $menu['items'] = $tree; |
||
| 161 | $this->remove_item_keys( $menu ); |
||
| 162 | return $menu; |
||
| 163 | } |
||
| 164 | |||
| 165 | // recursively ensure item lists are contiguous |
||
| 166 | View Code Duplication | protected function remove_item_keys( &$item ) { |
|
| 167 | if ( ! isset( $item['items'] ) || ! is_array( $item['items'] ) ) { |
||
| 168 | return; |
||
| 169 | } |
||
| 170 | |||
| 171 | |||
| 172 | foreach( $item['items'] as &$it ) { |
||
| 173 | $this->remove_item_keys( $it ); |
||
| 174 | } |
||
| 175 | |||
| 176 | $item['items'] = array_values( $item['items'] ); |
||
| 177 | } |
||
| 178 | |||
| 179 | protected function whitelist_and_rename_keys( $menus ) { |
||
| 180 | $transformed_menus = array(); |
||
| 181 | |||
| 182 | foreach ( $menus as $menu ) { |
||
| 183 | $menu = $this->whitelist_and_rename_with( $menu, $this->menu_whitelist ); |
||
| 184 | |||
| 185 | if ( isset( $menu['items'] ) ) { |
||
| 186 | foreach ( $menu['items'] as &$item ) { |
||
| 187 | $item = $this->whitelist_and_rename_with( $item, $this->menu_item_whitelist ); |
||
| 188 | } |
||
| 189 | } |
||
| 190 | |||
| 191 | $transformed_menus[] = $menu; |
||
| 192 | } |
||
| 193 | |||
| 194 | return $transformed_menus; |
||
| 195 | } |
||
| 196 | |||
| 197 | protected function add_locations( $menus ) { |
||
| 198 | $menus_with_locations = array(); |
||
| 199 | |||
| 200 | foreach( $menus as $menu ) { |
||
| 201 | $menu['locations'] = array_keys( get_nav_menu_locations(), $menu['id'] ); |
||
| 202 | $menus_with_locations[] = $menu; |
||
| 203 | } |
||
| 204 | |||
| 205 | return $menus_with_locations; |
||
| 206 | } |
||
| 207 | |||
| 208 | protected function add_widget_locations( $menus ) { |
||
| 209 | $nav_menu_widgets = WPCOM_JSON_API_Menus_Widgets::get(); |
||
| 210 | |||
| 211 | if ( ! is_array( $nav_menu_widgets ) ) { |
||
| 212 | return $menus; |
||
| 213 | } |
||
| 214 | |||
| 215 | foreach ( $menus as &$menu ) { |
||
| 216 | $widget_locations = array(); |
||
| 217 | |||
| 218 | View Code Duplication | foreach ( $nav_menu_widgets as $key => $widget ) { |
|
| 219 | if ( is_array( $widget ) && isset( $widget['nav_menu'] ) && |
||
| 220 | $widget['nav_menu'] === $menu['id'] ) { |
||
| 221 | $widget_locations[] = 'nav_menu_widget-' . $key; |
||
| 222 | } |
||
| 223 | } |
||
| 224 | $menu['locations'] = array_merge( $menu['locations'], $widget_locations ); |
||
| 225 | } |
||
| 226 | |||
| 227 | return $menus; |
||
| 228 | } |
||
| 229 | } |
||
| 230 | |||
| 231 | class WPCOM_JSON_API_Menus_Complexify extends WPCOM_JSON_API_Menus_Translator { |
||
| 232 | protected $filter = 'wpcom_menu_api_translator_complexify'; |
||
| 233 | |||
| 234 | protected $filters = array( |
||
| 235 | 'untreeify', |
||
| 236 | 'set_locations', |
||
| 237 | 'whitelist_and_rename_keys', |
||
| 238 | ); |
||
| 239 | |||
| 240 | protected $menu_whitelist = array( |
||
| 241 | 'id' => 'term_id', |
||
| 242 | 'name' => 'menu-name', |
||
| 243 | 'description' => 'description', |
||
| 244 | 'items' => 'items', |
||
| 245 | ); |
||
| 246 | |||
| 247 | protected $menu_item_whitelist = array( |
||
| 248 | 'id' => 'menu-item-db-id', |
||
| 249 | 'content_id' => 'menu-item-object-id', |
||
| 250 | 'type' => 'menu-item-object', |
||
| 251 | 'type_family' => 'menu-item-type', |
||
| 252 | 'type_label' => 'menu-item-type-label', |
||
| 253 | 'name' => 'menu-item-title', |
||
| 254 | 'order' => 'menu-item-position', |
||
| 255 | 'parent' => 'menu-item-parent-id', |
||
| 256 | 'url' => 'menu-item-url', |
||
| 257 | 'link_target' => 'menu-item-target', |
||
| 258 | 'link_title' => 'menu-item-attr-title', |
||
| 259 | 'status' => 'menu-item-status', |
||
| 260 | 'tmp_id' => 'tmp_id', |
||
| 261 | 'tmp_parent' => 'tmp_parent', |
||
| 262 | 'description' => 'menu-item-description', |
||
| 263 | 'classes' => 'menu-item-classes', |
||
| 264 | 'xfn' => 'menu-item-xfn', |
||
| 265 | ); |
||
| 266 | |||
| 267 | /************************** |
||
| 268 | * Filters methods |
||
| 269 | **************************/ |
||
| 270 | |||
| 271 | public function untreeify( $menus ) { |
||
| 272 | return array_map( array( $this, 'untreeify_menu' ), $menus ); |
||
| 273 | } |
||
| 274 | |||
| 275 | // convert the tree of menu items to a flat list suitable for |
||
| 276 | // the nav_menu APIs |
||
| 277 | protected function untreeify_menu( $menu ) { |
||
| 278 | if ( empty( $menu['items'] ) ) { |
||
| 279 | return $menu; |
||
| 280 | } |
||
| 281 | |||
| 282 | $items_list = array(); |
||
| 283 | $counter = 1; |
||
| 284 | foreach ( $menu['items'] as &$item ) { |
||
| 285 | $item[ 'parent' ] = 0; |
||
| 286 | } |
||
| 287 | $this->untreeify_items( $menu['items'], $items_list, $counter ); |
||
| 288 | $menu['items'] = $items_list; |
||
| 289 | |||
| 290 | return $menu; |
||
| 291 | } |
||
| 292 | |||
| 293 | /** |
||
| 294 | * Recurse the items tree adding each item to a flat list and restoring |
||
| 295 | * `order` and `parent` fields. |
||
| 296 | * |
||
| 297 | * @param array $items item tree |
||
| 298 | * @param array &$items_list output flat list of items |
||
| 299 | * @param int &$counter for creating temporary IDs |
||
| 300 | */ |
||
| 301 | protected function untreeify_items( $items, &$items_list, &$counter ) { |
||
| 302 | foreach( $items as $index => $item ) { |
||
| 303 | $item['order'] = $index + 1; |
||
| 304 | |||
| 305 | if( ! isset( $item['id'] ) ) { |
||
| 306 | $this->set_tmp_id( $item, $counter++ ); |
||
| 307 | } |
||
| 308 | |||
| 309 | if ( isset( $item['items'] ) && is_array( $item['items'] ) ) { |
||
| 310 | foreach ( $item['items'] as &$i ) { |
||
| 311 | $i['parent'] = $item['id']; |
||
| 312 | } |
||
| 313 | $this->untreeify_items( $item[ 'items' ], $items_list, $counter ); |
||
| 314 | unset( $item['items'] ); |
||
| 315 | } |
||
| 316 | |||
| 317 | $items_list[] = $item; |
||
| 318 | } |
||
| 319 | } |
||
| 320 | |||
| 321 | /** |
||
| 322 | * Populate `tmp_id` field for a new item, and `tmp_parent` field |
||
| 323 | * for all its children, to maintain the hierarchy. |
||
| 324 | * These fields will be used when creating |
||
| 325 | * new items with wp_update_nav_menu_item(). |
||
| 326 | */ |
||
| 327 | View Code Duplication | private function set_tmp_id( &$item, $tmp_id ) { |
|
| 328 | $item['tmp_id'] = $tmp_id; |
||
| 329 | if ( ! isset( $item['items'] ) || ! is_array( $item['items'] ) ) { |
||
| 330 | return; |
||
| 331 | } |
||
| 332 | foreach ( $item['items'] as &$child ) { |
||
| 333 | $child['tmp_parent'] = $tmp_id; |
||
| 334 | } |
||
| 335 | } |
||
| 336 | |||
| 337 | protected function whitelist_and_rename_keys( $menus ) { |
||
| 338 | $transformed_menus = array(); |
||
| 339 | foreach ( $menus as $menu ) { |
||
| 340 | $menu = $this->whitelist_and_rename_with( $menu, $this->menu_whitelist ); |
||
| 341 | if ( isset( $menu['items'] ) ) { |
||
| 342 | $menu['items'] = array_map( array( $this, 'whitelist_and_rename_item_keys' ), $menu['items'] ); |
||
| 343 | } |
||
| 344 | $transformed_menus[] = $menu; |
||
| 345 | } |
||
| 346 | |||
| 347 | return $transformed_menus; |
||
| 348 | } |
||
| 349 | |||
| 350 | protected function whitelist_and_rename_item_keys( $item ) { |
||
| 351 | $item = $this->implode_array_fields( $item ); |
||
| 352 | $item = $this->whitelist_and_rename_with( $item, $this->menu_item_whitelist ); |
||
| 353 | return $item; |
||
| 354 | } |
||
| 355 | |||
| 356 | // all item fields are set as strings |
||
| 357 | protected function implode_array_fields( $menu_item ) { |
||
| 358 | return array_map( array( $this, 'implode_array_field' ), $menu_item ); |
||
| 359 | } |
||
| 360 | |||
| 361 | protected function implode_array_field( $field ) { |
||
| 362 | if ( is_array( $field ) ) { |
||
| 363 | return implode( ' ', $field ); |
||
| 364 | } |
||
| 365 | return $field; |
||
| 366 | } |
||
| 367 | |||
| 368 | protected function set_locations( $menus ) { |
||
| 369 | foreach ( $menus as $menu ) { |
||
| 370 | if ( isset( $menu['locations'] ) ) { |
||
| 371 | if ( true !== $this->locations_are_valid( $menu['locations'] ) ) { |
||
| 372 | return $this->locations_are_valid( $menu['locations'] ); |
||
| 373 | } |
||
| 374 | } |
||
| 375 | } |
||
| 376 | |||
| 377 | return array_map( array( $this, 'set_location' ), $menus ); |
||
| 378 | } |
||
| 379 | |||
| 380 | protected function set_location( $menu ) { |
||
| 381 | $this->set_menu_at_locations( $menu['locations'], $menu['id'] ); |
||
| 382 | return $menu; |
||
| 383 | } |
||
| 384 | |||
| 385 | protected function set_menu_at_locations( $locations, $menu_id ) { |
||
| 386 | $location_map = get_nav_menu_locations(); |
||
| 387 | $this->remove_menu_from_all_locations( $menu_id, $location_map ); |
||
| 388 | |||
| 389 | if ( is_array( $locations ) ) { |
||
| 390 | foreach ( $locations as $location ) { |
||
| 391 | $location_map[ $location ] = $menu_id; |
||
| 392 | } |
||
| 393 | } |
||
| 394 | |||
| 395 | set_theme_mod( 'nav_menu_locations', $location_map ); |
||
| 396 | |||
| 397 | $this->set_widget_menu_at_locations( $locations, $menu_id ); |
||
| 398 | } |
||
| 399 | |||
| 400 | protected function remove_menu_from_all_locations( $menu_id, &$location_map ) { |
||
| 401 | foreach ( get_nav_menu_locations() as $existing_location => $existing_menu_id) { |
||
| 402 | if ( $existing_menu_id == $menu_id ) { |
||
| 403 | unset( $location_map[$existing_location] ); |
||
| 404 | } |
||
| 405 | } |
||
| 406 | } |
||
| 407 | |||
| 408 | protected function set_widget_menu_at_locations( $locations, $menu_id ) { |
||
| 409 | $nav_menu_widgets = get_option( 'widget_nav_menu' ); |
||
| 410 | |||
| 411 | if ( ! is_array( $nav_menu_widgets ) ) { |
||
| 412 | return; |
||
| 413 | } |
||
| 414 | |||
| 415 | // Remove menus from all custom menu widget locations |
||
| 416 | View Code Duplication | foreach ( $nav_menu_widgets as &$widget ) { |
|
| 417 | if ( is_array( $widget ) && isset( $widget['nav_menu'] ) && $widget['nav_menu'] == $menu_id ) { |
||
| 418 | $widget['nav_menu'] = 0; |
||
| 419 | } |
||
| 420 | } |
||
| 421 | |||
| 422 | if ( is_array( $locations ) ) { |
||
| 423 | foreach ( $locations as $location ) { |
||
| 424 | if ( preg_match( '/^nav_menu_widget-(\d+)/', $location, $matches ) ) { |
||
| 425 | if ( isset( $matches[1] ) ) { |
||
| 426 | $nav_menu_widgets[$matches[1]]['nav_menu'] = $menu_id; |
||
| 427 | } |
||
| 428 | } |
||
| 429 | } |
||
| 430 | } |
||
| 431 | |||
| 432 | update_option( 'widget_nav_menu', $nav_menu_widgets ); |
||
| 433 | } |
||
| 434 | |||
| 435 | protected function locations_are_valid( $locations ) { |
||
| 436 | if ( is_int( $locations ) ) { |
||
| 437 | if ( $locations != 0) { |
||
| 438 | return new WP_Error( 'locations-int', 'Locations int must be 0.', 400 ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'locations-int'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 439 | } else { |
||
| 440 | return true; |
||
| 441 | } |
||
| 442 | } elseif ( is_array( $locations ) ) { |
||
| 443 | foreach ( $locations as $location_name ) { |
||
| 444 | if ( ! $this->location_name_exists( $location_name ) ) { |
||
| 445 | return new WP_Error( 'locations-array', |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'locations-array'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 446 | sprintf( "Location '%s' does not exist.", $location_name ), 404 ); |
||
| 447 | } |
||
| 448 | } |
||
| 449 | return true; |
||
| 450 | } |
||
| 451 | return new WP_Error( 'locations', 'Locations must be array or integer.', 400 ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'locations'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 452 | } |
||
| 453 | |||
| 454 | protected function location_name_exists( $location_name ) { |
||
| 455 | $widget_location_names = wp_list_pluck( WPCOM_JSON_API_Menus_Widgets::get(), 'name' ); |
||
| 456 | |||
| 457 | $existing_locations = get_nav_menu_locations(); |
||
| 458 | |||
| 459 | if ( ! is_array( get_registered_nav_menus() ) ) { |
||
| 460 | return false; |
||
| 461 | } |
||
| 462 | |||
| 463 | return array_key_exists( $location_name, get_registered_nav_menus() ) || |
||
| 464 | array_key_exists( $location_name, $existing_locations ) || |
||
| 465 | in_array( $location_name, $widget_location_names ); |
||
| 466 | } |
||
| 467 | |||
| 468 | } |
||
| 469 | |||
| 470 | new WPCOM_JSON_API_Menus_New_Menu_Endpoint( array ( |
||
| 471 | 'method' => 'POST', |
||
| 472 | 'description' => 'Create a new navigation menu.', |
||
| 473 | 'group' => 'menus', |
||
| 474 | 'stat' => 'menus:new-menu', |
||
| 475 | 'path' => '/sites/%s/menus/new', |
||
| 476 | 'path_labels' => array( |
||
| 477 | '$site' => '(int|string) Site ID or domain', |
||
| 478 | ), |
||
| 479 | 'request_format' => array( |
||
| 480 | 'name' => '(string) Name of menu', |
||
| 481 | ), |
||
| 482 | 'response_format' => array( |
||
| 483 | 'id' => '(int) Newly created menu ID', |
||
| 484 | ), |
||
| 485 | 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/menus/new', |
||
| 486 | 'example_request_data' => array( |
||
| 487 | 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ), |
||
| 488 | 'body' => array( |
||
| 489 | 'name' => 'Menu 1' |
||
| 490 | ) |
||
| 491 | ), |
||
| 492 | ) ); |
||
| 493 | |||
| 494 | class WPCOM_JSON_API_Menus_New_Menu_Endpoint extends WPCOM_JSON_API_Menus_Abstract_Endpoint { |
||
| 495 | function callback( $path = '', $site = 0 ) { |
||
| 496 | $site_id = $this->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site ) ); |
||
| 497 | |||
| 498 | if ( is_wp_error( $site_id ) ) { |
||
| 499 | return $site_id; |
||
| 500 | } |
||
| 501 | |||
| 502 | $data = $this->input(); |
||
| 503 | |||
| 504 | $id = wp_create_nav_menu( $data['name'] ); |
||
| 505 | |||
| 506 | if ( is_wp_error( $id ) ) { |
||
| 507 | return $id; |
||
| 508 | } |
||
| 509 | |||
| 510 | return array( 'id' => $id ); |
||
| 511 | } |
||
| 512 | } |
||
| 513 | |||
| 514 | new WPCOM_JSON_API_Menus_Update_Menu_Endpoint( array ( |
||
| 515 | 'method' => 'POST', |
||
| 516 | 'description' => 'Update a navigation menu.', |
||
| 517 | 'group' => 'menus', |
||
| 518 | 'stat' => 'menus:update-menu', |
||
| 519 | 'path' => '/sites/%s/menus/%d', |
||
| 520 | 'path_labels' => array( |
||
| 521 | '$site' => '(int|string) Site ID or domain', |
||
| 522 | '$menu_id' => '(int) Menu ID', |
||
| 523 | ), |
||
| 524 | 'request_format' => array( |
||
| 525 | 'name' => '(string) Name of menu', |
||
| 526 | 'items' => '(array) A list of menu item objects. |
||
| 527 | <br/><br/> |
||
| 528 | Item objects contain fields relating to that item, e.g. id, type, content_id, |
||
| 529 | but they can also contain other items objects - this nesting represents parents |
||
| 530 | and child items in the item tree.' |
||
| 531 | ), |
||
| 532 | 'response_format' => array( |
||
| 533 | 'menu' => '(object) Updated menu object', |
||
| 534 | ), |
||
| 535 | 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/menus/510604099', |
||
| 536 | 'example_request_data' => array( |
||
| 537 | 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ), |
||
| 538 | 'body' => array( |
||
| 539 | 'name' => 'Test Menu' |
||
| 540 | ), |
||
| 541 | ), |
||
| 542 | ) ); |
||
| 543 | |||
| 544 | class WPCOM_JSON_API_Menus_Update_Menu_Endpoint extends WPCOM_JSON_API_Menus_Abstract_Endpoint { |
||
| 545 | function callback( $path = '', $site = 0, $menu_id = 0 ) { |
||
| 546 | $site_id = $this->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site ) ); |
||
| 547 | |||
| 548 | if ( is_wp_error( $site_id ) ) { |
||
| 549 | return $site_id; |
||
| 550 | } |
||
| 551 | |||
| 552 | if ( $menu_id <= 0 ) { |
||
| 553 | return new WP_Error( 'menu-id', 'Menu ID must be greater than 0.', 400 ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'menu-id'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 554 | } |
||
| 555 | |||
| 556 | $data = $this->input( true, false ); |
||
| 557 | $data['id'] = $menu_id; |
||
| 558 | $data = $this->complexify( array( $data ) ); |
||
| 559 | if ( is_wp_error( $data ) ) { |
||
| 560 | return $data; |
||
| 561 | } |
||
| 562 | $data = $data[0]; |
||
| 563 | |||
| 564 | // Avoid special-case handling of an unset 'items' field in empty menus |
||
| 565 | $data['items'] = isset( $data['items'] ) ? $data['items'] : array(); |
||
| 566 | |||
| 567 | $data = $this->create_new_items( $data, $menu_id ); |
||
| 568 | |||
| 569 | $result = wp_update_nav_menu_object( $menu_id, array( 'menu-name' => $data['menu-name'] ) ); |
||
| 570 | |||
| 571 | if ( is_wp_error( $result ) ) { |
||
| 572 | return $result; |
||
| 573 | } |
||
| 574 | |||
| 575 | $delete_status = $this->delete_items_not_present( $menu_id, $data['items'] ); |
||
| 576 | if( is_wp_error( $delete_status ) ) { |
||
| 577 | return $delete_status; |
||
| 578 | } |
||
| 579 | |||
| 580 | foreach ( $data['items'] as $item ) { |
||
| 581 | $item_id = isset( $item['menu-item-db-id'] ) ? $item['menu-item-db-id'] : 0; |
||
| 582 | $result = wp_update_nav_menu_item( $menu_id, $item_id, $item ); |
||
| 583 | if ( is_wp_error( $result ) ) { |
||
| 584 | return $result; |
||
| 585 | } |
||
| 586 | } |
||
| 587 | |||
| 588 | $items = wp_get_nav_menu_items( $menu_id, array( 'update_post_term_cache' => false ) ); |
||
| 589 | |||
| 590 | if ( is_wp_error( $items ) ) { |
||
| 591 | return $items; |
||
| 592 | } |
||
| 593 | |||
| 594 | $menu = wp_get_nav_menu_object( $menu_id ); |
||
| 595 | $menu->items = $items; |
||
| 596 | |||
| 597 | return array( 'menu' => $this->simplify( $menu ) ); |
||
| 598 | } |
||
| 599 | |||
| 600 | /** |
||
| 601 | * New items can have a 'tmp_id', allowing them to |
||
| 602 | * be used as parent items before they have been created. |
||
| 603 | * |
||
| 604 | * This function will create items that have a 'tmp_id' set, and |
||
| 605 | * update any items with a 'tmp_parent' to use the |
||
| 606 | * newly created item as a parent. |
||
| 607 | */ |
||
| 608 | function create_new_items( $data, $menu_id ) { |
||
| 609 | $tmp_to_actual_ids = array(); |
||
| 610 | foreach ( $data['items'] as &$item ) { |
||
| 611 | if ( isset( $item['tmp_id'] ) ) { |
||
| 612 | $actual_id = wp_update_nav_menu_item( $menu_id, 0, $item ); |
||
| 613 | $tmp_to_actual_ids[ $item['tmp_id'] ] = $actual_id; |
||
| 614 | unset( $item['tmp_id'] ); |
||
| 615 | $item['menu-item-db-id'] = $actual_id; |
||
| 616 | } |
||
| 617 | } |
||
| 618 | |||
| 619 | foreach ( $data['items'] as &$item ) { |
||
| 620 | if ( isset( $item['tmp_parent'] ) ) { |
||
| 621 | $item['menu-item-parent-id'] = $tmp_to_actual_ids[ $item['tmp_parent'] ]; |
||
| 622 | unset( $item['tmp_parent'] ); |
||
| 623 | } |
||
| 624 | } |
||
| 625 | |||
| 626 | return $data; |
||
| 627 | } |
||
| 628 | |||
| 629 | /** |
||
| 630 | * remove any existing menu items not present in the supplied array. |
||
| 631 | * returns wp_error if an item cannot be deleted. |
||
| 632 | */ |
||
| 633 | function delete_items_not_present( $menu_id, $menu_items ) { |
||
| 634 | |||
| 635 | $existing_items = wp_get_nav_menu_items( $menu_id, array( 'update_post_term_cache' => false ) ); |
||
| 636 | if ( ! is_array( $existing_items ) ) { |
||
| 637 | return true; |
||
| 638 | } |
||
| 639 | |||
| 640 | $existing_ids = wp_list_pluck( $existing_items, 'db_id' ); |
||
| 641 | $ids_to_keep = wp_list_pluck( $menu_items, 'menu-item-db-id' ); |
||
| 642 | $ids_to_remove = array_diff( $existing_ids, $ids_to_keep ); |
||
| 643 | |||
| 644 | foreach ( $ids_to_remove as $id ) { |
||
| 645 | if ( false === wp_delete_post( $id, true ) ) { |
||
| 646 | return new WP_Error( 'menu-item', |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'menu-item'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 647 | sprintf( 'Failed to delete menu item with id: %d.', $id ), 400 ); |
||
| 648 | } |
||
| 649 | } |
||
| 650 | |||
| 651 | return true; |
||
| 652 | } |
||
| 653 | } |
||
| 654 | |||
| 655 | new WPCOM_JSON_API_Menus_List_Menus_Endpoint( array ( |
||
| 656 | 'method'=> 'GET', |
||
| 657 | 'description' => 'Get a list of all navigation menus.', |
||
| 658 | 'group' => 'menus', |
||
| 659 | 'stat' => 'menus:list-menu', |
||
| 660 | 'path' => '/sites/%s/menus', |
||
| 661 | 'path_labels' => array( |
||
| 662 | '$site' => '(int|string) Site ID or domain', |
||
| 663 | ), |
||
| 664 | 'response_format' => array( |
||
| 665 | 'menus' => '(array) A list of menu objects.<br/><br/> |
||
| 666 | A menu object contains a name, items, locations, etc. |
||
| 667 | Check the example response for the full structure. |
||
| 668 | <br/><br/> |
||
| 669 | Item objects contain fields relating to that item, e.g. id, type, content_id, |
||
| 670 | but they can also contain other items objects - this nesting represents parents |
||
| 671 | and child items in the item tree.', |
||
| 672 | 'locations' => '(array) Locations where menus can be placed. List of objects, one per location.' |
||
| 673 | ), |
||
| 674 | 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/menus', |
||
| 675 | 'example_request_data' => array( |
||
| 676 | 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ), |
||
| 677 | ), |
||
| 678 | ) ); |
||
| 679 | |||
| 680 | class WPCOM_JSON_API_Menus_List_Menus_Endpoint extends WPCOM_JSON_API_Menus_Abstract_Endpoint { |
||
| 681 | function callback( $path = '', $site = 0 ) { |
||
| 682 | $site_id = $this->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site ) ); |
||
| 683 | |||
| 684 | if ( is_wp_error( $site_id ) ) { |
||
| 685 | return $site_id; |
||
| 686 | } |
||
| 687 | |||
| 688 | $menus = wp_get_nav_menus( array( 'orderby' => 'term_id' ) ); |
||
| 689 | |||
| 690 | if ( is_wp_error( $menus ) ) { |
||
| 691 | return $menus; |
||
| 692 | } |
||
| 693 | |||
| 694 | foreach ( $menus as $m ) { |
||
| 695 | $items = wp_get_nav_menu_items( $m->term_id, array( 'update_post_term_cache' => false ) ); |
||
| 696 | if ( is_wp_error( $items ) ) { |
||
| 697 | return $items; |
||
| 698 | } |
||
| 699 | $m->items = $items; |
||
| 700 | } |
||
| 701 | |||
| 702 | $menus = $this->simplify( $menus ); |
||
| 703 | |||
| 704 | if ( is_wp_error( $this->get_locations() ) ) { |
||
| 705 | return $this->get_locations(); |
||
| 706 | } |
||
| 707 | |||
| 708 | return array( 'menus' => $menus, 'locations' => $this->get_locations() ); |
||
| 709 | } |
||
| 710 | } |
||
| 711 | |||
| 712 | new WPCOM_JSON_API_Menus_Get_Menu_Endpoint( array ( |
||
| 713 | 'method'=> 'GET', |
||
| 714 | 'description' => 'Get a single navigation menu.', |
||
| 715 | 'group' => 'menus', |
||
| 716 | 'stat' => 'menus:get-menu', |
||
| 717 | 'path' => '/sites/%s/menus/%d', |
||
| 718 | 'path_labels' => array( |
||
| 719 | '$site' => '(int|string) Site ID or domain', |
||
| 720 | '$menu_id' => '(int) Menu ID', |
||
| 721 | ), |
||
| 722 | 'response_format' => array( |
||
| 723 | 'menu' => '(object) A menu object.<br/><br/> |
||
| 724 | A menu object contains a name, items, locations, etc. |
||
| 725 | Check the example response for the full structure. |
||
| 726 | <br/><br/> |
||
| 727 | Item objects contain fields relating to that item, e.g. id, type, content_id, |
||
| 728 | but they can also contain other items objects - this nesting represents parents |
||
| 729 | and child items in the item tree.' |
||
| 730 | ), |
||
| 731 | 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/menus/510604099', |
||
| 732 | 'example_request_data' => array( |
||
| 733 | 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ), |
||
| 734 | ), |
||
| 735 | ) ); |
||
| 736 | |||
| 737 | class WPCOM_JSON_API_Menus_Get_Menu_Endpoint extends WPCOM_JSON_API_Menus_Abstract_Endpoint { |
||
| 738 | function callback( $path = '', $site = 0, $menu_id = 0 ) { |
||
| 739 | $site_id = $this->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site ) ); |
||
| 740 | |||
| 741 | if ( is_wp_error( $site_id ) ) { |
||
| 742 | return $site_id; |
||
| 743 | } |
||
| 744 | |||
| 745 | if ( $menu_id <= 0 ) { |
||
| 746 | return new WP_Error( 'menu-id', 'Menu ID must be greater than 0.', 400 ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'menu-id'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 747 | } |
||
| 748 | |||
| 749 | $menu = get_term( $menu_id, 'nav_menu' ); |
||
| 750 | |||
| 751 | if ( is_wp_error( $menu ) ) { |
||
| 752 | return $menu; |
||
| 753 | } |
||
| 754 | |||
| 755 | $items = wp_get_nav_menu_items( $menu_id, array( 'update_post_term_cache' => false ) ); |
||
| 756 | |||
| 757 | if ( is_wp_error( $items ) ) { |
||
| 758 | return $items; |
||
| 759 | } |
||
| 760 | |||
| 761 | $menu->items = $items; |
||
| 762 | |||
| 763 | return array( 'menu' => $this->simplify( $menu ) ); |
||
| 764 | } |
||
| 765 | } |
||
| 766 | |||
| 767 | new WPCOM_JSON_API_Menus_Delete_Menu_Endpoint( array ( |
||
| 768 | 'method' => 'POST', |
||
| 769 | 'description' => 'Delete a navigation menu', |
||
| 770 | 'group' => 'menus', |
||
| 771 | 'stat' => 'menus:delete-menu', |
||
| 772 | 'path' => '/sites/%s/menus/%d/delete', |
||
| 773 | 'path_labels' => array( |
||
| 774 | '$site' => '(int|string) Site ID or domain', |
||
| 775 | '$menu_id' => '(int) Menu ID', |
||
| 776 | ), |
||
| 777 | 'response_format' => array( |
||
| 778 | 'deleted' => '(bool) Has the menu been deleted?', |
||
| 779 | ), |
||
| 780 | 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/menus/$menu_id/delete', |
||
| 781 | 'example_request_data' => array( |
||
| 782 | 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ), |
||
| 783 | ), |
||
| 784 | ) ); |
||
| 785 | |||
| 786 | class WPCOM_JSON_API_Menus_Delete_Menu_Endpoint extends WPCOM_JSON_API_Menus_Abstract_Endpoint { |
||
| 787 | function callback( $path = '', $site = 0, $menu_id = 0 ) { |
||
| 788 | $site_id = $this->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site ) ); |
||
| 789 | |||
| 790 | if ( is_wp_error( $site_id ) ) { |
||
| 791 | return $site_id; |
||
| 792 | } |
||
| 793 | |||
| 794 | if ( $menu_id <= 0 ) { |
||
| 795 | return new WP_Error( 'menu-id', 'Menu ID must be greater than 0.', 400 ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'menu-id'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 796 | } |
||
| 797 | |||
| 798 | $result = wp_delete_nav_menu( $menu_id ); |
||
| 799 | if ( ! is_wp_error( $result ) ) { |
||
| 800 | $result = array( 'deleted' => $result ); |
||
| 801 | } |
||
| 802 | |||
| 803 | return $result; |
||
| 804 | } |
||
| 805 | } |
||
| 806 | |||
| 807 | class WPCOM_JSON_API_Menus_Widgets { |
||
| 808 | static function get() { |
||
| 809 | $locations = array(); |
||
| 810 | $nav_menu_widgets = get_option( 'widget_nav_menu' ); |
||
| 811 | |||
| 812 | if ( ! is_array( $nav_menu_widgets ) ) { |
||
| 813 | return $locations; |
||
| 814 | } |
||
| 815 | |||
| 816 | foreach ( $nav_menu_widgets as $k => $v ) { |
||
| 817 | if ( is_array( $v ) && isset( $v['title'] ) ) { |
||
| 818 | $locations[$k] = array( 'name' => 'nav_menu_widget-' . $k, 'description' => $v['title'] ); |
||
| 819 | } |
||
| 820 | } |
||
| 821 | |||
| 822 | return $locations; |
||
| 823 | } |
||
| 824 | } |
||
| 825 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.