Issues (4967)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/wp-includes/nav-menu.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Navigation Menu functions
4
 *
5
 * @package WordPress
6
 * @subpackage Nav_Menus
7
 * @since 3.0.0
8
 */
9
10
/**
11
 * Returns a navigation menu object.
12
 *
13
 * @since 3.0.0
14
 *
15
 * @param int|string|WP_Term $menu Menu ID, slug, or name - or the menu object.
16
 * @return WP_Term|false False if $menu param isn't supplied or term does not exist, menu object if successful.
17
 */
18
function wp_get_nav_menu_object( $menu ) {
19
	$menu_obj = false;
20
21
	if ( is_object( $menu ) ) {
22
		$menu_obj = $menu;
23
	}
24
25
	if ( $menu && ! $menu_obj ) {
26
		$menu_obj = get_term( $menu, 'nav_menu' );
27
28
		if ( ! $menu_obj ) {
29
			$menu_obj = get_term_by( 'slug', $menu, 'nav_menu' );
30
		}
31
32
		if ( ! $menu_obj ) {
33
			$menu_obj = get_term_by( 'name', $menu, 'nav_menu' );
34
		}
35
	}
36
37
	if ( ! $menu_obj || is_wp_error( $menu_obj ) ) {
38
		$menu_obj = false;
39
	}
40
41
	/**
42
	 * Filters the nav_menu term retrieved for wp_get_nav_menu_object().
43
	 *
44
	 * @since 4.3.0
45
	 *
46
	 * @param object|false $menu_obj Term from nav_menu taxonomy, or false if nothing had been found.
47
	 * @param string       $menu     The menu ID, slug, or name passed to wp_get_nav_menu_object().
48
	 */
49
	return apply_filters( 'wp_get_nav_menu_object', $menu_obj, $menu );
50
}
51
52
/**
53
 * Check if the given ID is a navigation menu.
54
 *
55
 * Returns true if it is; false otherwise.
56
 *
57
 * @since 3.0.0
58
 *
59
 * @param int|string $menu The menu to check (ID, slug, or name).
60
 * @return bool Whether the menu exists.
61
 */
62
function is_nav_menu( $menu ) {
63
	if ( ! $menu )
64
		return false;
65
66
	$menu_obj = wp_get_nav_menu_object( $menu );
67
68
	if (
0 ignored issues
show
This if statement, and the following return statement can be replaced with return $menu_obj && !is_...== $menu_obj->taxonomy;.
Loading history...
69
		$menu_obj &&
70
		! is_wp_error( $menu_obj ) &&
71
		! empty( $menu_obj->taxonomy ) &&
72
		'nav_menu' == $menu_obj->taxonomy
73
	)
74
		return true;
75
76
	return false;
77
}
78
79
/**
80
 * Registers navigation menu locations for a theme.
81
 *
82
 * @since 3.0.0
83
 *
84
 * @global array $_wp_registered_nav_menus
85
 *
86
 * @param array $locations Associative array of menu location identifiers (like a slug) and descriptive text.
87
 */
88
function register_nav_menus( $locations = array() ) {
89
	global $_wp_registered_nav_menus;
90
91
	add_theme_support( 'menus' );
92
93
	$_wp_registered_nav_menus = array_merge( (array) $_wp_registered_nav_menus, $locations );
94
}
95
96
/**
97
 * Unregisters a navigation menu location for a theme.
98
 *
99
 * @since 3.1.0
100
 * @global array $_wp_registered_nav_menus
101
 *
102
 * @param string $location The menu location identifier.
103
 * @return bool True on success, false on failure.
104
 */
105
function unregister_nav_menu( $location ) {
106
	global $_wp_registered_nav_menus;
107
108
	if ( is_array( $_wp_registered_nav_menus ) && isset( $_wp_registered_nav_menus[$location] ) ) {
109
		unset( $_wp_registered_nav_menus[$location] );
110
		if ( empty( $_wp_registered_nav_menus ) ) {
111
			_remove_theme_support( 'menus' );
112
		}
113
		return true;
114
	}
115
	return false;
116
}
117
118
/**
119
 * Registers a navigation menu location for a theme.
120
 *
121
 * @since 3.0.0
122
 *
123
 * @param string $location    Menu location identifier, like a slug.
124
 * @param string $description Menu location descriptive text.
125
 */
126
function register_nav_menu( $location, $description ) {
127
	register_nav_menus( array( $location => $description ) );
128
}
129
/**
130
 * Retrieves all registered navigation menu locations in a theme.
131
 *
132
 * @since 3.0.0
133
 *
134
 * @global array $_wp_registered_nav_menus
135
 *
136
 * @return array Registered navigation menu locations. If none are registered, an empty array.
137
 */
138
function get_registered_nav_menus() {
139
	global $_wp_registered_nav_menus;
140
	if ( isset( $_wp_registered_nav_menus ) )
141
		return $_wp_registered_nav_menus;
142
	return array();
143
}
144
145
/**
146
 * Retrieves all registered navigation menu locations and the menus assigned to them.
147
 *
148
 * @since 3.0.0
149
 *
150
 * @return array Registered navigation menu locations and the menus assigned them.
151
 *               If none are registered, an empty array.
152
 */
153
154
function get_nav_menu_locations() {
155
	$locations = get_theme_mod( 'nav_menu_locations' );
156
	return ( is_array( $locations ) ) ? $locations : array();
157
}
158
159
/**
160
 * Determines whether a registered nav menu location has a menu assigned to it.
161
 *
162
 * @since 3.0.0
163
 *
164
 * @param string $location Menu location identifier.
165
 * @return bool Whether location has a menu.
166
 */
167
function has_nav_menu( $location ) {
168
	$has_nav_menu = false;
169
170
	$registered_nav_menus = get_registered_nav_menus();
171
	if ( isset( $registered_nav_menus[ $location ] ) ) {
172
		$locations = get_nav_menu_locations();
173
		$has_nav_menu = ! empty( $locations[ $location ] );
174
	}
175
176
	/**
177
	 * Filters whether a nav menu is assigned to the specified location.
178
	 *
179
	 * @since 4.3.0
180
	 *
181
	 * @param bool   $has_nav_menu Whether there is a menu assigned to a location.
182
	 * @param string $location     Menu location.
183
	 */
184
	return apply_filters( 'has_nav_menu', $has_nav_menu, $location );
185
}
186
187
/**
188
 * Determines whether the given ID is a nav menu item.
189
 *
190
 * @since 3.0.0
191
 *
192
 * @param int $menu_item_id The ID of the potential nav menu item.
193
 * @return bool Whether the given ID is that of a nav menu item.
194
 */
195
function is_nav_menu_item( $menu_item_id = 0 ) {
196
	return ( ! is_wp_error( $menu_item_id ) && ( 'nav_menu_item' == get_post_type( $menu_item_id ) ) );
197
}
198
199
/**
200
 * Creates a navigation menu.
201
 *
202
 * Note that `$menu_name` is expected to be pre-slashed.
203
 *
204
 * @since 3.0.0
205
 *
206
 * @param string $menu_name Menu name.
207
 * @return int|WP_Error Menu ID on success, WP_Error object on failure.
0 ignored issues
show
Should the return type not be WP_Error|array|integer|WP_Term|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
208
 */
209
function wp_create_nav_menu( $menu_name ) {
210
	// expected_slashed ($menu_name)
211
	return wp_update_nav_menu_object( 0, array( 'menu-name' => $menu_name ) );
212
}
213
214
/**
215
 * Delete a Navigation Menu.
216
 *
217
 * @since 3.0.0
218
 *
219
 * @param string $menu Menu ID, slug, or name.
220
 * @return bool|WP_Error True on success, false or WP_Error object on failure.
0 ignored issues
show
Should the return type not be boolean|integer|WP_Error?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
221
 */
222
function wp_delete_nav_menu( $menu ) {
223
	$menu = wp_get_nav_menu_object( $menu );
224
	if ( ! $menu )
225
		return false;
226
227
	$menu_objects = get_objects_in_term( $menu->term_id, 'nav_menu' );
228
	if ( ! empty( $menu_objects ) ) {
229
		foreach ( $menu_objects as $item ) {
0 ignored issues
show
The expression $menu_objects of type object<WP_Error>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
230
			wp_delete_post( $item );
231
		}
232
	}
233
234
	$result = wp_delete_term( $menu->term_id, 'nav_menu' );
235
236
	// Remove this menu from any locations.
237
	$locations = get_nav_menu_locations();
238
	foreach ( $locations as $location => $menu_id ) {
239
		if ( $menu_id == $menu->term_id )
240
			$locations[ $location ] = 0;
241
	}
242
	set_theme_mod( 'nav_menu_locations', $locations );
243
244
	if ( $result && !is_wp_error($result) )
245
246
		/**
247
		 * Fires after a navigation menu has been successfully deleted.
248
		 *
249
		 * @since 3.0.0
250
		 *
251
		 * @param int $term_id ID of the deleted menu.
252
		 */
253
		do_action( 'wp_delete_nav_menu', $menu->term_id );
254
255
	return $result;
256
}
257
258
/**
259
 * Save the properties of a menu or create a new menu with those properties.
260
 *
261
 * Note that `$menu_data` is expected to be pre-slashed.
262
 *
263
 * @since 3.0.0
264
 *
265
 * @param int   $menu_id   The ID of the menu or "0" to create a new menu.
266
 * @param array $menu_data The array of menu data.
267
 * @return int|WP_Error Menu ID on success, WP_Error object on failure.
0 ignored issues
show
Should the return type not be WP_Error|array|integer|WP_Term|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
268
 */
269
function wp_update_nav_menu_object( $menu_id = 0, $menu_data = array() ) {
270
	// expected_slashed ($menu_data)
271
	$menu_id = (int) $menu_id;
272
273
	$_menu = wp_get_nav_menu_object( $menu_id );
274
275
	$args = array(
276
		'description' => ( isset( $menu_data['description'] ) ? $menu_data['description']  : '' ),
277
		'name'        => ( isset( $menu_data['menu-name']   ) ? $menu_data['menu-name']    : '' ),
278
		'parent'      => ( isset( $menu_data['parent']      ) ? (int) $menu_data['parent'] : 0  ),
279
		'slug'        => null,
280
	);
281
282
	// double-check that we're not going to have one menu take the name of another
283
	$_possible_existing = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
284
	if (
285
		$_possible_existing &&
286
		! is_wp_error( $_possible_existing ) &&
287
		isset( $_possible_existing->term_id ) &&
288
		$_possible_existing->term_id != $menu_id
289
	) {
290
		return new WP_Error( 'menu_exists',
291
			/* translators: %s: menu name */
292
			sprintf( __( 'The menu name %s conflicts with another menu name. Please try another.' ),
293
				'<strong>' . esc_html( $menu_data['menu-name'] ) . '</strong>'
294
			)
295
		);
296
	}
297
298
	// menu doesn't already exist, so create a new menu
299
	if ( ! $_menu || is_wp_error( $_menu ) ) {
300
		$menu_exists = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
301
302
		if ( $menu_exists ) {
303
			return new WP_Error( 'menu_exists',
304
				/* translators: %s: menu name */
305
				sprintf( __( 'The menu name %s conflicts with another menu name. Please try another.' ),
306
					'<strong>' . esc_html( $menu_data['menu-name'] ) . '</strong>'
307
				)
308
			);
309
		}
310
311
		$_menu = wp_insert_term( $menu_data['menu-name'], 'nav_menu', $args );
312
313
		if ( is_wp_error( $_menu ) )
314
			return $_menu;
315
316
		/**
317
		 * Fires after a navigation menu is successfully created.
318
		 *
319
		 * @since 3.0.0
320
		 *
321
		 * @param int   $term_id   ID of the new menu.
322
		 * @param array $menu_data An array of menu data.
323
		 */
324
		do_action( 'wp_create_nav_menu', $_menu['term_id'], $menu_data );
325
326
		return (int) $_menu['term_id'];
327
	}
328
329
	if ( ! $_menu || ! isset( $_menu->term_id ) )
330
		return 0;
331
332
	$menu_id = (int) $_menu->term_id;
333
334
	$update_response = wp_update_term( $menu_id, 'nav_menu', $args );
335
336
	if ( is_wp_error( $update_response ) )
337
		return $update_response;
338
339
	$menu_id = (int) $update_response['term_id'];
340
341
	/**
342
	 * Fires after a navigation menu has been successfully updated.
343
	 *
344
	 * @since 3.0.0
345
	 *
346
	 * @param int   $menu_id   ID of the updated menu.
347
	 * @param array $menu_data An array of menu data.
348
	 */
349
	do_action( 'wp_update_nav_menu', $menu_id, $menu_data );
350
	return $menu_id;
351
}
352
353
/**
354
 * Save the properties of a menu item or create a new one.
355
 *
356
 * The menu-item-title, menu-item-description, and menu-item-attr-title are expected
357
 * to be pre-slashed since they are passed directly into `wp_insert_post()`.
358
 *
359
 * @since 3.0.0
360
 *
361
 * @param int   $menu_id         The ID of the menu. Required. If "0", makes the menu item a draft orphan.
362
 * @param int   $menu_item_db_id The ID of the menu item. If "0", creates a new menu item.
363
 * @param array $menu_item_data  The menu item's data.
364
 * @return int|WP_Error The menu item's database ID or WP_Error object on failure.
0 ignored issues
show
Should the return type not be WP_Error|WP_Term|false|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
365
 */
366
function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item_data = array() ) {
367
	$menu_id = (int) $menu_id;
368
	$menu_item_db_id = (int) $menu_item_db_id;
369
370
	// make sure that we don't convert non-nav_menu_item objects into nav_menu_item objects
371
	if ( ! empty( $menu_item_db_id ) && ! is_nav_menu_item( $menu_item_db_id ) )
372
		return new WP_Error( 'update_nav_menu_item_failed', __( 'The given object ID is not that of a menu item.' ) );
373
374
	$menu = wp_get_nav_menu_object( $menu_id );
375
376
	if ( ! $menu && 0 !== $menu_id ) {
377
		return new WP_Error( 'invalid_menu_id', __( 'Invalid menu ID.' ) );
378
	}
379
380
	if ( is_wp_error( $menu ) ) {
381
		return $menu;
382
	}
383
384
	$defaults = array(
385
		'menu-item-db-id' => $menu_item_db_id,
386
		'menu-item-object-id' => 0,
387
		'menu-item-object' => '',
388
		'menu-item-parent-id' => 0,
389
		'menu-item-position' => 0,
390
		'menu-item-type' => 'custom',
391
		'menu-item-title' => '',
392
		'menu-item-url' => '',
393
		'menu-item-description' => '',
394
		'menu-item-attr-title' => '',
395
		'menu-item-target' => '',
396
		'menu-item-classes' => '',
397
		'menu-item-xfn' => '',
398
		'menu-item-status' => '',
399
	);
400
401
	$args = wp_parse_args( $menu_item_data, $defaults );
402
403
	if ( 0 == $menu_id ) {
404
		$args['menu-item-position'] = 1;
405
	} elseif ( 0 == (int) $args['menu-item-position'] ) {
406
		$menu_items = 0 == $menu_id ? array() : (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) );
407
		$last_item = array_pop( $menu_items );
408
		$args['menu-item-position'] = ( $last_item && isset( $last_item->menu_order ) ) ? 1 + $last_item->menu_order : count( $menu_items );
409
	}
410
411
	$original_parent = 0 < $menu_item_db_id ? get_post_field( 'post_parent', $menu_item_db_id ) : 0;
412
413
	if ( 'custom' != $args['menu-item-type'] ) {
414
		/* if non-custom menu item, then:
415
			* use original object's URL
416
			* blank default title to sync with original object's
417
		*/
418
419
		$args['menu-item-url'] = '';
420
421
		$original_title = '';
422
		if ( 'taxonomy' == $args['menu-item-type'] ) {
423
			$original_parent = get_term_field( 'parent', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );
424
			$original_title = get_term_field( 'name', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );
425
		} elseif ( 'post_type' == $args['menu-item-type'] ) {
426
427
			$original_object = get_post( $args['menu-item-object-id'] );
428
			$original_parent = (int) $original_object->post_parent;
429
			$original_title = $original_object->post_title;
430
		} elseif ( 'post_type_archive' == $args['menu-item-type'] ) {
431
			$original_object = get_post_type_object( $args['menu-item-object'] );
432
			if ( $original_object ) {
433
				$original_title = $original_object->labels->archives;
434
			}
435
		}
436
437
		if ( $args['menu-item-title'] == $original_title )
438
			$args['menu-item-title'] = '';
439
440
		// hack to get wp to create a post object when too many properties are empty
441
		if ( '' ==  $args['menu-item-title'] && '' == $args['menu-item-description'] )
442
			$args['menu-item-description'] = ' ';
443
	}
444
445
	// Populate the menu item object
446
	$post = array(
447
		'menu_order' => $args['menu-item-position'],
448
		'ping_status' => 0,
449
		'post_content' => $args['menu-item-description'],
450
		'post_excerpt' => $args['menu-item-attr-title'],
451
		'post_parent' => $original_parent,
452
		'post_title' => $args['menu-item-title'],
453
		'post_type' => 'nav_menu_item',
454
	);
455
456
	$update = 0 != $menu_item_db_id;
457
458
	// New menu item. Default is draft status
459
	if ( ! $update ) {
460
		$post['ID'] = 0;
461
		$post['post_status'] = 'publish' == $args['menu-item-status'] ? 'publish' : 'draft';
462
		$menu_item_db_id = wp_insert_post( $post );
463
		if ( ! $menu_item_db_id	|| is_wp_error( $menu_item_db_id ) )
464
			return $menu_item_db_id;
465
466
		/**
467
		 * Fires immediately after a new navigation menu item has been added.
468
		 *
469
		 * @since 4.4.0
470
		 *
471
		 * @see wp_update_nav_menu_item()
472
		 *
473
		 * @param int   $menu_id         ID of the updated menu.
474
		 * @param int   $menu_item_db_id ID of the new menu item.
475
		 * @param array $args            An array of arguments used to update/add the menu item.
476
		 */
477
		do_action( 'wp_add_nav_menu_item', $menu_id, $menu_item_db_id, $args );
478
	}
479
480
	// Associate the menu item with the menu term
481
	// Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms()
482
	 if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) {
483
		wp_set_object_terms( $menu_item_db_id, array( $menu->term_id ), 'nav_menu' );
484
	}
485
486
	if ( 'custom' == $args['menu-item-type'] ) {
487
		$args['menu-item-object-id'] = $menu_item_db_id;
488
		$args['menu-item-object'] = 'custom';
489
	}
490
491
	$menu_item_db_id = (int) $menu_item_db_id;
492
493
	update_post_meta( $menu_item_db_id, '_menu_item_type', sanitize_key($args['menu-item-type']) );
494
	update_post_meta( $menu_item_db_id, '_menu_item_menu_item_parent', strval( (int) $args['menu-item-parent-id'] ) );
495
	update_post_meta( $menu_item_db_id, '_menu_item_object_id', strval( (int) $args['menu-item-object-id'] ) );
496
	update_post_meta( $menu_item_db_id, '_menu_item_object', sanitize_key($args['menu-item-object']) );
497
	update_post_meta( $menu_item_db_id, '_menu_item_target', sanitize_key($args['menu-item-target']) );
498
499
	$args['menu-item-classes'] = array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-classes'] ) );
500
	$args['menu-item-xfn'] = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-xfn'] ) ) );
501
	update_post_meta( $menu_item_db_id, '_menu_item_classes', $args['menu-item-classes'] );
502
	update_post_meta( $menu_item_db_id, '_menu_item_xfn', $args['menu-item-xfn'] );
503
	update_post_meta( $menu_item_db_id, '_menu_item_url', esc_url_raw($args['menu-item-url']) );
504
505
	if ( 0 == $menu_id )
506
		update_post_meta( $menu_item_db_id, '_menu_item_orphaned', (string) time() );
507
	elseif ( get_post_meta( $menu_item_db_id, '_menu_item_orphaned' ) )
508
		delete_post_meta( $menu_item_db_id, '_menu_item_orphaned' );
509
510
	// Update existing menu item. Default is publish status
511
	if ( $update ) {
512
		$post['ID'] = $menu_item_db_id;
513
		$post['post_status'] = 'draft' == $args['menu-item-status'] ? 'draft' : 'publish';
514
		wp_update_post( $post );
515
	}
516
517
	/**
518
	 * Fires after a navigation menu item has been updated.
519
	 *
520
	 * @since 3.0.0
521
	 *
522
	 * @see wp_update_nav_menu_item()
523
	 *
524
	 * @param int   $menu_id         ID of the updated menu.
525
	 * @param int   $menu_item_db_id ID of the updated menu item.
526
	 * @param array $args            An array of arguments used to update a menu item.
527
	 */
528
	do_action( 'wp_update_nav_menu_item', $menu_id, $menu_item_db_id, $args );
529
530
	return $menu_item_db_id;
531
}
532
533
/**
534
 * Returns all navigation menu objects.
535
 *
536
 * @since 3.0.0
537
 * @since 4.1.0 Default value of the 'orderby' argument was changed from 'none'
538
 *              to 'name'.
539
 *
540
 * @param array $args Optional. Array of arguments passed on to get_terms().
541
 *                    Default empty array.
542
 * @return array Menu objects.
543
 */
544
function wp_get_nav_menus( $args = array() ) {
545
	$defaults = array( 'hide_empty' => false, 'orderby' => 'name' );
546
	$args = wp_parse_args( $args, $defaults );
547
548
	/**
549
	 * Filters the navigation menu objects being returned.
550
	 *
551
	 * @since 3.0.0
552
	 *
553
	 * @see get_terms()
554
	 *
555
	 * @param array $menus An array of menu objects.
556
	 * @param array $args  An array of arguments used to retrieve menu objects.
557
	 */
558
	return apply_filters( 'wp_get_nav_menus', get_terms( 'nav_menu',  $args), $args );
559
}
560
561
/**
562
 * Return if a menu item is valid.
563
 *
564
 * @link https://core.trac.wordpress.org/ticket/13958
565
 *
566
 * @since 3.2.0
567
 * @access private
568
 *
569
 * @param object $item The menu item to check.
570
 * @return bool False if invalid, otherwise true.
571
 */
572
function _is_valid_nav_menu_item( $item ) {
573
	return empty( $item->_invalid );
574
}
575
576
/**
577
 * Return all menu items of a navigation menu.
578
 *
579
 * @since 3.0.0
580
 *
581
 * @global string $_menu_item_sort_prop
582
 * @staticvar array $fetched
583
 *
584
 * @param string $menu Menu name, ID, or slug.
585
 * @param array  $args Optional. Arguments to pass to get_posts().
586
 * @return false|array $items Array of menu items, otherwise false.
587
 */
588
function wp_get_nav_menu_items( $menu, $args = array() ) {
589
	$menu = wp_get_nav_menu_object( $menu );
590
591
	if ( ! $menu ) {
592
		return false;
593
	}
594
595
	static $fetched = array();
596
597
	$items = get_objects_in_term( $menu->term_id, 'nav_menu' );
598
	if ( is_wp_error( $items ) ) {
599
		return false;
600
	}
601
602
	$defaults = array( 'order' => 'ASC', 'orderby' => 'menu_order', 'post_type' => 'nav_menu_item',
603
		'post_status' => 'publish', 'output' => ARRAY_A, 'output_key' => 'menu_order', 'nopaging' => true );
604
	$args = wp_parse_args( $args, $defaults );
605
	$args['include'] = $items;
606
607
	if ( ! empty( $items ) ) {
608
		$items = get_posts( $args );
609
	} else {
610
		$items = array();
611
	}
612
613
	// Get all posts and terms at once to prime the caches
614
	if ( empty( $fetched[$menu->term_id] ) || wp_using_ext_object_cache() ) {
615
		$fetched[$menu->term_id] = true;
616
		$posts = array();
617
		$terms = array();
618
		foreach ( $items as $item ) {
619
			$object_id = get_post_meta( $item->ID, '_menu_item_object_id', true );
620
			$object    = get_post_meta( $item->ID, '_menu_item_object',    true );
621
			$type      = get_post_meta( $item->ID, '_menu_item_type',      true );
622
623
			if ( 'post_type' == $type )
624
				$posts[$object][] = $object_id;
625
			elseif ( 'taxonomy' == $type)
626
				$terms[$object][] = $object_id;
627
		}
628
629
		if ( ! empty( $posts ) ) {
630
			foreach ( array_keys($posts) as $post_type ) {
631
				get_posts( array('post__in' => $posts[$post_type], 'post_type' => $post_type, 'nopaging' => true, 'update_post_term_cache' => false) );
632
			}
633
		}
634
		unset($posts);
635
636
		if ( ! empty( $terms ) ) {
637
			foreach ( array_keys($terms) as $taxonomy ) {
638
				get_terms( $taxonomy, array(
639
					'include' => $terms[ $taxonomy ],
640
					'hierarchical' => false,
641
				) );
642
			}
643
		}
644
		unset($terms);
645
	}
646
647
	$items = array_map( 'wp_setup_nav_menu_item', $items );
648
649
	if ( ! is_admin() ) { // Remove invalid items only in front end
650
		$items = array_filter( $items, '_is_valid_nav_menu_item' );
651
	}
652
653 View Code Duplication
	if ( ARRAY_A == $args['output'] ) {
654
		$items = wp_list_sort( $items, array(
655
			$args['output_key'] => 'ASC',
656
		) );
657
		$i = 1;
658
		foreach ( $items as $k => $item ) {
659
			$items[$k]->{$args['output_key']} = $i++;
660
		}
661
	}
662
663
	/**
664
	 * Filters the navigation menu items being returned.
665
	 *
666
	 * @since 3.0.0
667
	 *
668
	 * @param array  $items An array of menu item post objects.
669
	 * @param object $menu  The menu object.
670
	 * @param array  $args  An array of arguments used to retrieve menu item objects.
671
	 */
672
	return apply_filters( 'wp_get_nav_menu_items', $items, $menu, $args );
673
}
674
675
/**
676
 * Decorates a menu item object with the shared navigation menu item properties.
677
 *
678
 * Properties:
679
 * - ID:               The term_id if the menu item represents a taxonomy term.
680
 * - attr_title:       The title attribute of the link element for this menu item.
681
 * - classes:          The array of class attribute values for the link element of this menu item.
682
 * - db_id:            The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist).
683
 * - description:      The description of this menu item.
684
 * - menu_item_parent: The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise.
685
 * - object:           The type of object originally represented, such as "category," "post", or "attachment."
686
 * - object_id:        The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories.
687
 * - post_parent:      The DB ID of the original object's parent object, if any (0 otherwise).
688
 * - post_title:       A "no title" label if menu item represents a post that lacks a title.
689
 * - target:           The target attribute of the link element for this menu item.
690
 * - title:            The title of this menu item.
691
 * - type:             The family of objects originally represented, such as "post_type" or "taxonomy."
692
 * - type_label:       The singular label used to describe this type of menu item.
693
 * - url:              The URL to which this menu item points.
694
 * - xfn:              The XFN relationship expressed in the link of this menu item.
695
 * - _invalid:         Whether the menu item represents an object that no longer exists.
696
 *
697
 * @since 3.0.0
698
 *
699
 * @param object $menu_item The menu item to modify.
700
 * @return object $menu_item The menu item with standard menu item properties.
701
 */
702
function wp_setup_nav_menu_item( $menu_item ) {
703
	if ( isset( $menu_item->post_type ) ) {
704
		if ( 'nav_menu_item' == $menu_item->post_type ) {
705
			$menu_item->db_id = (int) $menu_item->ID;
706
			$menu_item->menu_item_parent = ! isset( $menu_item->menu_item_parent ) ? get_post_meta( $menu_item->ID, '_menu_item_menu_item_parent', true ) : $menu_item->menu_item_parent;
707
			$menu_item->object_id = ! isset( $menu_item->object_id ) ? get_post_meta( $menu_item->ID, '_menu_item_object_id', true ) : $menu_item->object_id;
708
			$menu_item->object = ! isset( $menu_item->object ) ? get_post_meta( $menu_item->ID, '_menu_item_object', true ) : $menu_item->object;
709
			$menu_item->type = ! isset( $menu_item->type ) ? get_post_meta( $menu_item->ID, '_menu_item_type', true ) : $menu_item->type;
710
711
			if ( 'post_type' == $menu_item->type ) {
712
				$object = get_post_type_object( $menu_item->object );
713 View Code Duplication
				if ( $object ) {
714
					$menu_item->type_label = $object->labels->singular_name;
715
				} else {
716
					$menu_item->type_label = $menu_item->object;
717
					$menu_item->_invalid = true;
718
				}
719
720
				if ( 'trash' === get_post_status( $menu_item->object_id ) ) {
721
					$menu_item->_invalid = true;
722
				}
723
724
				$menu_item->url = get_permalink( $menu_item->object_id );
725
726
				$original_object = get_post( $menu_item->object_id );
727
				/** This filter is documented in wp-includes/post-template.php */
728
				$original_title = apply_filters( 'the_title', $original_object->post_title, $original_object->ID );
729
730
				if ( '' === $original_title ) {
731
					/* translators: %d: ID of a post */
732
					$original_title = sprintf( __( '#%d (no title)' ), $original_object->ID );
733
				}
734
735
				$menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
736
737
			} elseif ( 'post_type_archive' == $menu_item->type ) {
738
				$object =  get_post_type_object( $menu_item->object );
739
				if ( $object ) {
740
					$menu_item->title = '' == $menu_item->post_title ? $object->labels->archives : $menu_item->post_title;
741
					$post_type_description = $object->description;
742
				} else {
743
					$menu_item->_invalid = true;
744
					$post_type_description = '';
745
				}
746
747
				$menu_item->type_label = __( 'Post Type Archive' );
748
				$post_content = wp_trim_words( $menu_item->post_content, 200 );
749
				$post_type_description = '' == $post_content ? $post_type_description : $post_content;
750
				$menu_item->url = get_post_type_archive_link( $menu_item->object );
751
			} elseif ( 'taxonomy' == $menu_item->type ) {
752
				$object = get_taxonomy( $menu_item->object );
753 View Code Duplication
				if ( $object ) {
754
					$menu_item->type_label = $object->labels->singular_name;
755
				} else {
756
					$menu_item->type_label = $menu_item->object;
757
					$menu_item->_invalid = true;
758
				}
759
760
				$term_url = get_term_link( (int) $menu_item->object_id, $menu_item->object );
761
				$menu_item->url = !is_wp_error( $term_url ) ? $term_url : '';
762
763
				$original_title = get_term_field( 'name', $menu_item->object_id, $menu_item->object, 'raw' );
764
				if ( is_wp_error( $original_title ) )
765
					$original_title = false;
766
				$menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
767
768
			} else {
769
				$menu_item->type_label = __('Custom Link');
770
				$menu_item->title = $menu_item->post_title;
771
				$menu_item->url = ! isset( $menu_item->url ) ? get_post_meta( $menu_item->ID, '_menu_item_url', true ) : $menu_item->url;
772
			}
773
774
			$menu_item->target = ! isset( $menu_item->target ) ? get_post_meta( $menu_item->ID, '_menu_item_target', true ) : $menu_item->target;
775
776
			/**
777
			 * Filters a navigation menu item's title attribute.
778
			 *
779
			 * @since 3.0.0
780
			 *
781
			 * @param string $item_title The menu item title attribute.
782
			 */
783
			$menu_item->attr_title = ! isset( $menu_item->attr_title ) ? apply_filters( 'nav_menu_attr_title', $menu_item->post_excerpt ) : $menu_item->attr_title;
784
785
			if ( ! isset( $menu_item->description ) ) {
786
				/**
787
				 * Filters a navigation menu item's description.
788
				 *
789
				 * @since 3.0.0
790
				 *
791
				 * @param string $description The menu item description.
792
				 */
793
				$menu_item->description = apply_filters( 'nav_menu_description', wp_trim_words( $menu_item->post_content, 200 ) );
794
			}
795
796
			$menu_item->classes = ! isset( $menu_item->classes ) ? (array) get_post_meta( $menu_item->ID, '_menu_item_classes', true ) : $menu_item->classes;
797
			$menu_item->xfn = ! isset( $menu_item->xfn ) ? get_post_meta( $menu_item->ID, '_menu_item_xfn', true ) : $menu_item->xfn;
798
		} else {
799
			$menu_item->db_id = 0;
800
			$menu_item->menu_item_parent = 0;
801
			$menu_item->object_id = (int) $menu_item->ID;
802
			$menu_item->type = 'post_type';
803
804
			$object = get_post_type_object( $menu_item->post_type );
805
			$menu_item->object = $object->name;
806
			$menu_item->type_label = $object->labels->singular_name;
807
808
			if ( '' === $menu_item->post_title ) {
809
				/* translators: %d: ID of a post */
810
				$menu_item->post_title = sprintf( __( '#%d (no title)' ), $menu_item->ID );
811
			}
812
813
			$menu_item->title = $menu_item->post_title;
814
			$menu_item->url = get_permalink( $menu_item->ID );
815
			$menu_item->target = '';
816
817
			/** This filter is documented in wp-includes/nav-menu.php */
818
			$menu_item->attr_title = apply_filters( 'nav_menu_attr_title', '' );
819
820
			/** This filter is documented in wp-includes/nav-menu.php */
821
			$menu_item->description = apply_filters( 'nav_menu_description', '' );
822
			$menu_item->classes = array();
823
			$menu_item->xfn = '';
824
		}
825
	} elseif ( isset( $menu_item->taxonomy ) ) {
826
		$menu_item->ID = $menu_item->term_id;
827
		$menu_item->db_id = 0;
828
		$menu_item->menu_item_parent = 0;
829
		$menu_item->object_id = (int) $menu_item->term_id;
830
		$menu_item->post_parent = (int) $menu_item->parent;
831
		$menu_item->type = 'taxonomy';
832
833
		$object = get_taxonomy( $menu_item->taxonomy );
834
		$menu_item->object = $object->name;
835
		$menu_item->type_label = $object->labels->singular_name;
836
837
		$menu_item->title = $menu_item->name;
838
		$menu_item->url = get_term_link( $menu_item, $menu_item->taxonomy );
839
		$menu_item->target = '';
840
		$menu_item->attr_title = '';
841
		$menu_item->description = get_term_field( 'description', $menu_item->term_id, $menu_item->taxonomy );
842
		$menu_item->classes = array();
843
		$menu_item->xfn = '';
844
845
	}
846
847
	/**
848
	 * Filters a navigation menu item object.
849
	 *
850
	 * @since 3.0.0
851
	 *
852
	 * @param object $menu_item The menu item object.
853
	 */
854
	return apply_filters( 'wp_setup_nav_menu_item', $menu_item );
855
}
856
857
/**
858
 * Get the menu items associated with a particular object.
859
 *
860
 * @since 3.0.0
861
 *
862
 * @param int    $object_id   The ID of the original object.
863
 * @param string $object_type The type of object, such as "taxonomy" or "post_type."
864
 * @param string $taxonomy    If $object_type is "taxonomy", $taxonomy is the name of the tax that $object_id belongs to
865
 * @return array The array of menu item IDs; empty array if none;
866
 */
867
function wp_get_associated_nav_menu_items( $object_id = 0, $object_type = 'post_type', $taxonomy = '' ) {
868
	$object_id = (int) $object_id;
869
	$menu_item_ids = array();
870
871
	$query = new WP_Query;
872
	$menu_items = $query->query(
873
		array(
874
			'meta_key' => '_menu_item_object_id',
875
			'meta_value' => $object_id,
876
			'post_status' => 'any',
877
			'post_type' => 'nav_menu_item',
878
			'posts_per_page' => -1,
879
		)
880
	);
881
	foreach ( (array) $menu_items as $menu_item ) {
882
		if ( isset( $menu_item->ID ) && is_nav_menu_item( $menu_item->ID ) ) {
883
			$menu_item_type = get_post_meta( $menu_item->ID, '_menu_item_type', true );
884
			if (
885
				'post_type' == $object_type &&
886
				'post_type' == $menu_item_type
887
			) {
888
				$menu_item_ids[] = (int) $menu_item->ID;
889
			} elseif (
890
				'taxonomy' == $object_type &&
891
				'taxonomy' == $menu_item_type &&
892
				get_post_meta( $menu_item->ID, '_menu_item_object', true ) == $taxonomy
893
			) {
894
				$menu_item_ids[] = (int) $menu_item->ID;
895
			}
896
		}
897
	}
898
899
	return array_unique( $menu_item_ids );
900
}
901
902
/**
903
 * Callback for handling a menu item when its original object is deleted.
904
 *
905
 * @since 3.0.0
906
 * @access private
907
 *
908
 * @param int $object_id The ID of the original object being trashed.
909
 *
910
 */
911 View Code Duplication
function _wp_delete_post_menu_item( $object_id = 0 ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
912
	$object_id = (int) $object_id;
913
914
	$menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'post_type' );
915
916
	foreach ( (array) $menu_item_ids as $menu_item_id ) {
917
		wp_delete_post( $menu_item_id, true );
918
	}
919
}
920
921
/**
922
 * Serves as a callback for handling a menu item when its original object is deleted.
923
 *
924
 * @since 3.0.0
925
 * @access private
926
 *
927
 * @param int    $object_id Optional. The ID of the original object being trashed. Default 0.
928
 * @param int    $tt_id     Term taxonomy ID. Unused.
929
 * @param string $taxonomy  Taxonomy slug.
930
 */
931 View Code Duplication
function _wp_delete_tax_menu_item( $object_id = 0, $tt_id, $taxonomy ) {
0 ignored issues
show
The parameter $tt_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
932
	$object_id = (int) $object_id;
933
934
	$menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'taxonomy', $taxonomy );
935
936
	foreach ( (array) $menu_item_ids as $menu_item_id ) {
937
		wp_delete_post( $menu_item_id, true );
938
	}
939
}
940
941
/**
942
 * Automatically add newly published page objects to menus with that as an option.
943
 *
944
 * @since 3.0.0
945
 * @access private
946
 *
947
 * @param string $new_status The new status of the post object.
948
 * @param string $old_status The old status of the post object.
949
 * @param object $post       The post object being transitioned from one status to another.
950
 */
951
function _wp_auto_add_pages_to_menu( $new_status, $old_status, $post ) {
952
	if ( 'publish' != $new_status || 'publish' == $old_status || 'page' != $post->post_type )
953
		return;
954
	if ( ! empty( $post->post_parent ) )
955
		return;
956
	$auto_add = get_option( 'nav_menu_options' );
957
	if ( empty( $auto_add ) || ! is_array( $auto_add ) || ! isset( $auto_add['auto_add'] ) )
958
		return;
959
	$auto_add = $auto_add['auto_add'];
960
	if ( empty( $auto_add ) || ! is_array( $auto_add ) )
961
		return;
962
963
	$args = array(
964
		'menu-item-object-id' => $post->ID,
965
		'menu-item-object' => $post->post_type,
966
		'menu-item-type' => 'post_type',
967
		'menu-item-status' => 'publish',
968
	);
969
970
	foreach ( $auto_add as $menu_id ) {
971
		$items = wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) );
972
		if ( ! is_array( $items ) )
973
			continue;
974
		foreach ( $items as $item ) {
975
			if ( $post->ID == $item->object_id )
976
				continue 2;
977
		}
978
		wp_update_nav_menu_item( $menu_id, 0, $args );
979
	}
980
}
981
982
/**
983
 * Delete auto-draft posts associated with the supplied changeset.
984
 *
985
 * @since 4.8.0
986
 * @access private
987
 *
988
 * @param int $post_id Post ID for the customize_changeset.
989
 */
990
function _wp_delete_customize_changeset_dependent_auto_drafts( $post_id ) {
991
	$post = get_post( $post_id );
992
993
	if ( ! $post || 'customize_changeset' !== $post->post_type ) {
994
		return;
995
	}
996
997
	$data = json_decode( $post->post_content, true );
998
	if ( empty( $data['nav_menus_created_posts']['value'] ) ) {
999
		return;
1000
	}
1001
	remove_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
1002
	foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) {
1003
		if ( ! empty( $post_id ) && 'auto-draft' === get_post_status( $post_id ) ) {
1004
			wp_delete_post( $post_id, true );
1005
		}
1006
	}
1007
	add_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
1008
}
1009