run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
23.8 KB
2026-03-11 16:18:51
R W Run
7.8 KB
2026-03-11 16:18:52
R W Run
36.1 KB
2026-03-11 16:18:51
R W Run
11.9 KB
2026-03-11 16:18:52
R W Run
18.94 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:52
R W Run
28.6 KB
2026-03-11 16:18:51
R W Run
316 By
2026-03-11 16:18:51
R W Run
12.9 KB
2026-03-11 16:18:51
R W Run
61.02 KB
2026-03-11 16:18:52
R W Run
15 KB
2026-03-11 16:18:51
R W Run
112.05 KB
2026-03-11 16:18:51
R W Run
12.47 KB
2026-03-11 16:18:51
R W Run
15.07 KB
2026-03-11 16:18:52
R W Run
9.84 KB
2026-03-11 16:18:52
R W Run
13.17 KB
2026-03-11 16:18:52
R W Run
33.83 KB
2026-03-11 16:18:51
R W Run
42.63 KB
2026-03-11 16:18:51
R W Run
55.71 KB
2026-03-11 16:18:52
R W Run
12.53 KB
2026-03-11 16:18:51
R W Run
2.55 KB
2026-03-11 16:18:52
R W Run
28.92 KB
2026-03-11 16:18:52
R W Run
539 By
2026-03-11 16:18:51
R W Run
367 By
2026-03-11 16:18:52
R W Run
42.65 KB
2026-03-11 16:18:51
R W Run
401 By
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
664 By
2026-03-11 16:18:52
R W Run
20.63 KB
2026-03-11 16:18:51
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
453 By
2026-03-11 16:18:52
R W Run
457 By
2026-03-11 16:18:51
R W Run
36.83 KB
2026-03-11 16:18:52
R W Run
2.41 KB
2026-03-11 16:18:52
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
13.89 KB
2026-03-11 16:18:51
R W Run
11.76 KB
2026-03-11 16:18:51
R W Run
2.65 KB
2026-03-11 16:18:51
R W Run
7.43 KB
2026-03-11 16:18:51
R W Run
17.46 KB
2026-03-11 16:18:51
R W Run
5.14 KB
2026-03-11 16:18:52
R W Run
16.7 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:52
R W Run
2.92 KB
2026-03-11 16:18:52
R W Run
1.32 KB
2026-03-11 16:18:51
R W Run
4.6 KB
2026-03-11 16:18:52
R W Run
11.62 KB
2026-03-11 16:18:52
R W Run
2.5 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
11.25 KB
2026-03-11 16:18:52
R W Run
5.32 KB
2026-03-11 16:18:51
R W Run
10.99 KB
2026-03-11 16:18:52
R W Run
68.32 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:51
R W Run
5.49 KB
2026-03-11 16:18:51
R W Run
1.99 KB
2026-03-11 16:18:52
R W Run
7.02 KB
2026-03-11 16:18:51
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
16.86 KB
2026-03-11 16:18:51
R W Run
24.23 KB
2026-03-11 16:18:51
R W Run
3.97 KB
2026-03-11 16:18:51
R W Run
47.66 KB
2026-03-11 16:18:51
R W Run
9.22 KB
2026-03-11 16:18:51
R W Run
25.51 KB
2026-03-11 16:18:51
R W Run
198.38 KB
2026-03-11 16:18:52
R W Run
56.65 KB
2026-03-11 16:18:51
R W Run
10.46 KB
2026-03-11 16:18:51
R W Run
10.95 KB
2026-03-11 16:18:52
R W Run
29.26 KB
2026-03-11 16:18:51
R W Run
70.91 KB
2026-03-11 16:18:52
R W Run
35.3 KB
2026-03-11 16:18:52
R W Run
16.61 KB
2026-03-11 16:18:52
R W Run
2.57 KB
2026-03-11 16:18:52
R W Run
39.83 KB
2026-03-11 16:18:51
R W Run
70.64 KB
2026-03-11 16:18:51
R W Run
15.56 KB
2026-03-11 16:18:52
R W Run
7.33 KB
2026-03-11 16:18:52
R W Run
253 By
2026-03-11 16:18:51
R W Run
7.96 KB
2026-03-11 16:18:52
R W Run
3.23 KB
2026-03-11 16:18:52
R W Run
969 By
2026-03-11 16:18:52
R W Run
16.28 KB
2026-03-11 16:18:51
R W Run
7.22 KB
2026-03-11 16:18:51
R W Run
12.95 KB
2026-03-11 16:18:51
R W Run
6.53 KB
2026-03-11 16:18:51
R W Run
3.42 KB
2026-03-11 16:18:52
R W Run
5.84 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
4.3 KB
2026-03-11 16:18:52
R W Run
2.91 KB
2026-03-11 16:18:51
R W Run
16.46 KB
2026-03-11 16:18:52
R W Run
40.6 KB
2026-03-11 16:18:51
R W Run
20.22 KB
2026-03-11 16:18:51
R W Run
36.11 KB
2026-03-11 16:18:52
R W Run
17.01 KB
2026-03-11 16:18:51
R W Run
7.27 KB
2026-03-11 16:18:52
R W Run
6.62 KB
2026-03-11 16:18:52
R W Run
16.49 KB
2026-03-11 16:18:52
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
29.82 KB
2026-03-11 16:18:51
R W Run
6.67 KB
2026-03-11 16:18:52
R W Run
8.98 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:51
R W Run
12.01 KB
2026-03-11 16:18:51
R W Run
17.11 KB
2026-03-11 16:18:51
R W Run
6.74 KB
2026-03-11 16:18:52
R W Run
30.93 KB
2026-03-11 16:18:51
R W Run
4.99 KB
2026-03-11 16:18:51
R W Run
4.25 KB
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
29.96 KB
2026-03-11 16:18:52
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
160 KB
2026-03-11 16:18:51
R W Run
6.72 KB
2026-03-11 16:18:52
R W Run
10.92 KB
2026-03-11 16:18:51
R W Run
4.77 KB
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
11.18 KB
2026-03-11 16:18:51
R W Run
62.19 KB
2026-03-11 16:18:51
R W Run
2.46 KB
2026-03-11 16:18:51
R W Run
9.17 KB
2026-03-11 16:18:51
R W Run
32.15 KB
2026-03-11 16:18:51
R W Run
34.05 KB
2026-03-11 16:18:52
R W Run
7.15 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:52
R W Run
1.87 KB
2026-03-11 16:18:52
R W Run
30.91 KB
2026-03-11 16:18:51
R W Run
7.29 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:51
R W Run
12.54 KB
2026-03-11 16:18:51
R W Run
19.12 KB
2026-03-11 16:18:51
R W Run
18.12 KB
2026-03-11 16:18:52
R W Run
39.99 KB
2026-03-11 16:18:52
R W Run
5.17 KB
2026-03-11 16:18:52
R W Run
979 By
2026-03-11 16:18:51
R W Run
18.44 KB
2026-03-11 16:18:52
R W Run
10.24 KB
2026-03-11 16:18:51
R W Run
1.77 KB
2026-03-11 16:18:52
R W Run
34.9 KB
2026-03-11 16:18:51
R W Run
7.19 KB
2026-03-11 16:18:52
R W Run
160.5 KB
2026-03-11 16:18:51
R W Run
64.27 KB
2026-03-11 16:18:51
R W Run
27.95 KB
2026-03-11 16:18:51
R W Run
4.69 KB
2026-03-11 16:18:51
R W Run
2.94 KB
2026-03-11 16:18:51
R W Run
43.13 KB
2026-03-11 16:18:52
R W Run
2.25 KB
2026-03-11 16:18:52
R W Run
22.5 KB
2026-03-11 16:18:51
R W Run
13.01 KB
2026-03-11 16:18:52
R W Run
3.27 KB
2026-03-11 16:18:51
R W Run
18 KB
2026-03-11 16:18:51
R W Run
210.4 KB
2026-03-11 16:18:52
R W Run
25.86 KB
2026-03-11 16:18:52
R W Run
115.85 KB
2026-03-11 16:18:51
R W Run
373 By
2026-03-11 16:18:52
R W Run
343 By
2026-03-11 16:18:52
R W Run
338 By
2026-03-11 16:18:51
R W Run
100.73 KB
2026-03-11 16:18:52
R W Run
130.93 KB
2026-03-11 16:18:51
R W Run
19.1 KB
2026-03-11 16:18:51
R W Run
17.41 KB
2026-03-11 16:18:52
R W Run
41.98 KB
2026-03-11 16:18:52
R W Run
400 By
2026-03-11 16:18:52
R W Run
11.1 KB
2026-03-11 16:18:52
R W Run
37.02 KB
2026-03-11 16:18:51
R W Run
2.24 KB
2026-03-11 16:18:51
R W Run
188.13 KB
2026-03-11 16:18:51
R W Run
338 By
2026-03-11 16:18:51
R W Run
38 KB
2026-03-11 16:18:51
R W Run
4.02 KB
2026-03-11 16:18:52
R W Run
5.38 KB
2026-03-11 16:18:51
R W Run
3.05 KB
2026-03-11 16:18:52
R W Run
2.61 KB
2026-03-11 16:18:51
R W Run
1.16 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
24.6 KB
2026-03-11 16:18:51
R W Run
9.56 KB
2026-03-11 16:18:51
R W Run
346.43 KB
2026-03-11 16:18:52
R W Run
281.84 KB
2026-03-11 16:18:52
R W Run
14.95 KB
2026-03-11 16:18:51
R W Run
8.44 KB
2026-03-11 16:18:52
R W Run
168.95 KB
2026-03-11 16:18:52
R W Run
20.71 KB
2026-03-11 16:18:52
R W Run
25.27 KB
2026-03-11 16:18:51
R W Run
5.72 KB
2026-03-11 16:18:51
R W Run
4.63 KB
2026-03-11 16:18:52
R W Run
81.73 KB
2026-03-11 16:18:51
R W Run
67.18 KB
2026-03-11 16:18:51
R W Run
156.36 KB
2026-03-11 16:18:52
R W Run
55.19 KB
2026-03-11 16:18:51
R W Run
162 By
2026-03-11 16:18:51
R W Run
61.72 KB
2026-03-11 16:18:51
R W Run
216.06 KB
2026-03-11 16:18:52
R W Run
65.09 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:52
R W Run
4.81 KB
2026-03-11 16:18:51
R W Run
6.48 KB
2026-03-11 16:18:52
R W Run
21.25 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:52
R W Run
89.69 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
40.74 KB
2026-03-11 16:18:51
R W Run
25.38 KB
2026-03-11 16:18:51
R W Run
43.31 KB
2026-03-11 16:18:52
R W Run
102.57 KB
2026-03-11 16:18:52
R W Run
6.18 KB
2026-03-11 16:18:51
R W Run
124.47 KB
2026-03-11 16:18:52
R W Run
35.65 KB
2026-03-11 16:18:52
R W Run
6.94 KB
2026-03-11 16:18:52
R W Run
67.04 KB
2026-03-11 16:18:52
R W Run
10.62 KB
2026-03-11 16:18:51
R W Run
289.35 KB
2026-03-11 16:18:52
R W Run
36.23 KB
2026-03-11 16:18:51
R W Run
200 By
2026-03-11 16:18:52
R W Run
200 By
2026-03-11 16:18:52
R W Run
98.29 KB
2026-03-11 16:18:52
R W Run
30.02 KB
2026-03-11 16:18:52
R W Run
19.03 KB
2026-03-11 16:18:52
R W Run
5.06 KB
2026-03-11 16:18:52
R W Run
255 By
2026-03-11 16:18:51
R W Run
22.66 KB
2026-03-11 16:18:52
R W Run
154.63 KB
2026-03-11 16:18:51
R W Run
9.68 KB
2026-03-11 16:18:51
R W Run
258 By
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
3.16 KB
2026-03-11 16:18:51
R W Run
8.4 KB
2026-03-11 16:18:52
R W Run
441 By
2026-03-11 16:18:51
R W Run
7.39 KB
2026-03-11 16:18:51
R W Run
173 KB
2026-03-11 16:18:52
R W Run
544 By
2026-03-11 16:18:52
R W Run
4.17 KB
2026-03-11 16:18:51
R W Run
35.97 KB
2026-03-11 16:18:52
R W Run
1.69 KB
2026-03-11 16:18:51
R W Run
2.84 KB
2026-03-11 16:18:52
R W Run
6.09 KB
2026-03-11 16:18:51
R W Run
8.71 KB
2026-03-11 16:18:51
R W Run
131.84 KB
2026-03-11 16:18:51
R W Run
37.45 KB
2026-03-11 16:18:51
R W Run
173.89 KB
2026-03-11 16:18:51
R W Run
7.09 KB
2026-03-11 16:18:51
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
1.08 KB
2026-03-11 16:18:51
R W Run
69.46 KB
2026-03-11 16:18:52
R W Run
445 By
2026-03-11 16:18:51
R W Run
799 By
2026-03-11 16:18:52
R W Run
error_log
📄nav-menu.php
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, name, or object.
16 * @return WP_Term|false Menu object on success, false if $menu param isn't supplied or term does not exist.
17 */
18function 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 WP_Term|false $menu_obj Term from nav_menu taxonomy, or false if nothing had been found.
47 * @param int|string|WP_Term $menu The menu ID, slug, name, or object passed to wp_get_nav_menu_object().
48 */
49 return apply_filters( 'wp_get_nav_menu_object', $menu_obj, $menu );
50}
51
52/**
53 * Determines whether 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|WP_Term $menu Menu ID, slug, name, or object of menu to check.
60 * @return bool Whether the menu exists.
61 */
62function is_nav_menu( $menu ) {
63 if ( ! $menu ) {
64 return false;
65 }
66
67 $menu_obj = wp_get_nav_menu_object( $menu );
68
69 if (
70 $menu_obj &&
71 ! is_wp_error( $menu_obj ) &&
72 ! empty( $menu_obj->taxonomy ) &&
73 'nav_menu' === $menu_obj->taxonomy
74 ) {
75 return true;
76 }
77
78 return false;
79}
80
81/**
82 * Registers navigation menu locations for a theme.
83 *
84 * @since 3.0.0
85 *
86 * @global array $_wp_registered_nav_menus
87 *
88 * @param string[] $locations Associative array of menu location identifiers (like a slug) and descriptive text.
89 */
90function register_nav_menus( $locations = array() ) {
91 global $_wp_registered_nav_menus;
92
93 add_theme_support( 'menus' );
94
95 foreach ( $locations as $key => $value ) {
96 if ( is_int( $key ) ) {
97 _doing_it_wrong( __FUNCTION__, __( 'Nav menu locations must be strings.' ), '5.3.0' );
98 break;
99 }
100 }
101
102 $_wp_registered_nav_menus = array_merge( (array) $_wp_registered_nav_menus, $locations );
103}
104
105/**
106 * Unregisters a navigation menu location for a theme.
107 *
108 * @since 3.1.0
109 *
110 * @global array $_wp_registered_nav_menus
111 *
112 * @param string $location The menu location identifier.
113 * @return bool True on success, false on failure.
114 */
115function unregister_nav_menu( $location ) {
116 global $_wp_registered_nav_menus;
117
118 if ( is_array( $_wp_registered_nav_menus ) && isset( $_wp_registered_nav_menus[ $location ] ) ) {
119 unset( $_wp_registered_nav_menus[ $location ] );
120 if ( empty( $_wp_registered_nav_menus ) ) {
121 _remove_theme_support( 'menus' );
122 }
123 return true;
124 }
125 return false;
126}
127
128/**
129 * Registers a navigation menu location for a theme.
130 *
131 * @since 3.0.0
132 *
133 * @param string $location Menu location identifier, like a slug.
134 * @param string $description Menu location descriptive text.
135 */
136function register_nav_menu( $location, $description ) {
137 register_nav_menus( array( $location => $description ) );
138}
139/**
140 * Retrieves all registered navigation menu locations in a theme.
141 *
142 * @since 3.0.0
143 *
144 * @global array $_wp_registered_nav_menus
145 *
146 * @return string[] Associative array of registered navigation menu descriptions keyed
147 * by their location. If none are registered, an empty array.
148 */
149function get_registered_nav_menus() {
150 global $_wp_registered_nav_menus;
151 if ( isset( $_wp_registered_nav_menus ) ) {
152 return $_wp_registered_nav_menus;
153 }
154 return array();
155}
156
157/**
158 * Retrieves all registered navigation menu locations and the menus assigned to them.
159 *
160 * @since 3.0.0
161 *
162 * @return int[] Associative array of registered navigation menu IDs keyed by their
163 * location name. If none are registered, an empty array.
164 */
165function get_nav_menu_locations() {
166 $locations = get_theme_mod( 'nav_menu_locations' );
167 return ( is_array( $locations ) ) ? $locations : array();
168}
169
170/**
171 * Determines whether a registered nav menu location has a menu assigned to it.
172 *
173 * @since 3.0.0
174 *
175 * @param string $location Menu location identifier.
176 * @return bool Whether location has a menu.
177 */
178function has_nav_menu( $location ) {
179 $has_nav_menu = false;
180
181 $registered_nav_menus = get_registered_nav_menus();
182 if ( isset( $registered_nav_menus[ $location ] ) ) {
183 $locations = get_nav_menu_locations();
184 $has_nav_menu = ! empty( $locations[ $location ] );
185 }
186
187 /**
188 * Filters whether a nav menu is assigned to the specified location.
189 *
190 * @since 4.3.0
191 *
192 * @param bool $has_nav_menu Whether there is a menu assigned to a location.
193 * @param string $location Menu location.
194 */
195 return apply_filters( 'has_nav_menu', $has_nav_menu, $location );
196}
197
198/**
199 * Returns the name of a navigation menu.
200 *
201 * @since 4.9.0
202 *
203 * @param string $location Menu location identifier.
204 * @return string Menu name.
205 */
206function wp_get_nav_menu_name( $location ) {
207 $menu_name = '';
208
209 $locations = get_nav_menu_locations();
210
211 if ( isset( $locations[ $location ] ) ) {
212 $menu = wp_get_nav_menu_object( $locations[ $location ] );
213
214 if ( $menu && $menu->name ) {
215 $menu_name = $menu->name;
216 }
217 }
218
219 /**
220 * Filters the navigation menu name being returned.
221 *
222 * @since 4.9.0
223 *
224 * @param string $menu_name Menu name.
225 * @param string $location Menu location identifier.
226 */
227 return apply_filters( 'wp_get_nav_menu_name', $menu_name, $location );
228}
229
230/**
231 * Determines whether the given ID is a nav menu item.
232 *
233 * @since 3.0.0
234 *
235 * @param int $menu_item_id The ID of the potential nav menu item.
236 * @return bool Whether the given ID is that of a nav menu item.
237 */
238function is_nav_menu_item( $menu_item_id = 0 ) {
239 return ( ! is_wp_error( $menu_item_id ) && ( 'nav_menu_item' === get_post_type( $menu_item_id ) ) );
240}
241
242/**
243 * Creates a navigation menu.
244 *
245 * Note that `$menu_name` is expected to be pre-slashed.
246 *
247 * @since 3.0.0
248 *
249 * @param string $menu_name Menu name.
250 * @return int|WP_Error Menu ID on success, WP_Error object on failure.
251 */
252function wp_create_nav_menu( $menu_name ) {
253 // expected_slashed ($menu_name)
254 return wp_update_nav_menu_object( 0, array( 'menu-name' => $menu_name ) );
255}
256
257/**
258 * Deletes a navigation menu.
259 *
260 * @since 3.0.0
261 *
262 * @param int|string|WP_Term $menu Menu ID, slug, name, or object.
263 * @return bool|WP_Error True on success, false or WP_Error object on failure.
264 */
265function wp_delete_nav_menu( $menu ) {
266 $menu = wp_get_nav_menu_object( $menu );
267 if ( ! $menu ) {
268 return false;
269 }
270
271 $menu_objects = get_objects_in_term( $menu->term_id, 'nav_menu' );
272 if ( ! empty( $menu_objects ) ) {
273 foreach ( $menu_objects as $item ) {
274 wp_delete_post( $item );
275 }
276 }
277
278 $result = wp_delete_term( $menu->term_id, 'nav_menu' );
279
280 // Remove this menu from any locations.
281 $locations = get_nav_menu_locations();
282 foreach ( $locations as $location => $menu_id ) {
283 if ( $menu_id === $menu->term_id ) {
284 $locations[ $location ] = 0;
285 }
286 }
287 set_theme_mod( 'nav_menu_locations', $locations );
288
289 if ( $result && ! is_wp_error( $result ) ) {
290
291 /**
292 * Fires after a navigation menu has been successfully deleted.
293 *
294 * @since 3.0.0
295 *
296 * @param int $term_id ID of the deleted menu.
297 */
298 do_action( 'wp_delete_nav_menu', $menu->term_id );
299 }
300
301 return $result;
302}
303
304/**
305 * Saves the properties of a menu or create a new menu with those properties.
306 *
307 * Note that `$menu_data` is expected to be pre-slashed.
308 *
309 * @since 3.0.0
310 *
311 * @param int $menu_id The ID of the menu or "0" to create a new menu.
312 * @param array $menu_data The array of menu data.
313 * @return int|WP_Error Menu ID on success, WP_Error object on failure.
314 */
315function wp_update_nav_menu_object( $menu_id = 0, $menu_data = array() ) {
316 // expected_slashed ($menu_data)
317 $menu_id = (int) $menu_id;
318
319 $_menu = wp_get_nav_menu_object( $menu_id );
320
321 $args = array(
322 'description' => ( isset( $menu_data['description'] ) ? $menu_data['description'] : '' ),
323 'name' => ( isset( $menu_data['menu-name'] ) ? $menu_data['menu-name'] : '' ),
324 'parent' => ( isset( $menu_data['parent'] ) ? (int) $menu_data['parent'] : 0 ),
325 'slug' => null,
326 );
327
328 // Double-check that we're not going to have one menu take the name of another.
329 $_possible_existing = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
330 if (
331 $_possible_existing &&
332 ! is_wp_error( $_possible_existing ) &&
333 isset( $_possible_existing->term_id ) &&
334 $_possible_existing->term_id !== $menu_id
335 ) {
336 return new WP_Error(
337 'menu_exists',
338 sprintf(
339 /* translators: %s: Menu name. */
340 __( 'The menu name %s conflicts with another menu name. Please try another.' ),
341 '<strong>' . esc_html( $menu_data['menu-name'] ) . '</strong>'
342 )
343 );
344 }
345
346 // Menu doesn't already exist, so create a new menu.
347 if ( ! $_menu || is_wp_error( $_menu ) ) {
348 $menu_exists = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
349
350 if ( $menu_exists ) {
351 return new WP_Error(
352 'menu_exists',
353 sprintf(
354 /* translators: %s: Menu name. */
355 __( 'The menu name %s conflicts with another menu name. Please try another.' ),
356 '<strong>' . esc_html( $menu_data['menu-name'] ) . '</strong>'
357 )
358 );
359 }
360
361 $_menu = wp_insert_term( $menu_data['menu-name'], 'nav_menu', $args );
362
363 if ( is_wp_error( $_menu ) ) {
364 return $_menu;
365 }
366
367 /**
368 * Fires after a navigation menu is successfully created.
369 *
370 * @since 3.0.0
371 *
372 * @param int $term_id ID of the new menu.
373 * @param array $menu_data An array of menu data.
374 */
375 do_action( 'wp_create_nav_menu', $_menu['term_id'], $menu_data );
376
377 return (int) $_menu['term_id'];
378 }
379
380 if ( ! $_menu || ! isset( $_menu->term_id ) ) {
381 return 0;
382 }
383
384 $menu_id = (int) $_menu->term_id;
385
386 $update_response = wp_update_term( $menu_id, 'nav_menu', $args );
387
388 if ( is_wp_error( $update_response ) ) {
389 return $update_response;
390 }
391
392 $menu_id = (int) $update_response['term_id'];
393
394 /**
395 * Fires after a navigation menu has been successfully updated.
396 *
397 * @since 3.0.0
398 *
399 * @param int $menu_id ID of the updated menu.
400 * @param array $menu_data An array of menu data.
401 */
402 do_action( 'wp_update_nav_menu', $menu_id, $menu_data );
403 return $menu_id;
404}
405
406/**
407 * Saves the properties of a menu item or create a new one.
408 *
409 * The menu-item-title, menu-item-description and menu-item-attr-title are expected
410 * to be pre-slashed since they are passed directly to APIs that expect slashed data.
411 *
412 * @since 3.0.0
413 * @since 5.9.0 Added the `$fire_after_hooks` parameter.
414 *
415 * @param int $menu_id The ID of the menu. If 0, makes the menu item a draft orphan.
416 * @param int $menu_item_db_id The ID of the menu item. If 0, creates a new menu item.
417 * @param array $menu_item_data The menu item's data.
418 * @param bool $fire_after_hooks Whether to fire the after insert hooks. Default true.
419 * @return int|WP_Error The menu item's database ID or WP_Error object on failure.
420 */
421function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item_data = array(), $fire_after_hooks = true ) {
422 $menu_id = (int) $menu_id;
423 $menu_item_db_id = (int) $menu_item_db_id;
424
425 // Make sure that we don't convert non-nav_menu_item objects into nav_menu_item objects.
426 if ( ! empty( $menu_item_db_id ) && ! is_nav_menu_item( $menu_item_db_id ) ) {
427 return new WP_Error( 'update_nav_menu_item_failed', __( 'The given object ID is not that of a menu item.' ) );
428 }
429
430 $menu = wp_get_nav_menu_object( $menu_id );
431
432 if ( ! $menu && 0 !== $menu_id ) {
433 return new WP_Error( 'invalid_menu_id', __( 'Invalid menu ID.' ) );
434 }
435
436 if ( is_wp_error( $menu ) ) {
437 return $menu;
438 }
439
440 $defaults = array(
441 'menu-item-db-id' => $menu_item_db_id,
442 'menu-item-object-id' => 0,
443 'menu-item-object' => '',
444 'menu-item-parent-id' => 0,
445 'menu-item-position' => 0,
446 'menu-item-type' => 'custom',
447 'menu-item-title' => '',
448 'menu-item-url' => '',
449 'menu-item-description' => '',
450 'menu-item-attr-title' => '',
451 'menu-item-target' => '',
452 'menu-item-classes' => '',
453 'menu-item-xfn' => '',
454 'menu-item-status' => '',
455 'menu-item-post-date' => '',
456 'menu-item-post-date-gmt' => '',
457 );
458
459 $args = wp_parse_args( $menu_item_data, $defaults );
460
461 if ( 0 === $menu_id ) {
462 $args['menu-item-position'] = 1;
463 } elseif ( 0 === (int) $args['menu-item-position'] ) {
464 $menu_items = array();
465
466 if ( 0 !== $menu_id ) {
467 $menu_items = (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) );
468 }
469
470 $last_item = array_pop( $menu_items );
471
472 if ( $last_item && isset( $last_item->menu_order ) ) {
473 $args['menu-item-position'] = 1 + $last_item->menu_order;
474 } else {
475 $args['menu-item-position'] = count( $menu_items );
476 }
477 }
478
479 $original_parent = 0 < $menu_item_db_id ? get_post_field( 'post_parent', $menu_item_db_id ) : 0;
480
481 if ( 'custom' === $args['menu-item-type'] ) {
482 // If custom menu item, trim the URL.
483 $args['menu-item-url'] = trim( $args['menu-item-url'] );
484 } else {
485 /*
486 * If non-custom menu item, then:
487 * - use the original object's URL.
488 * - blank default title to sync with the original object's title.
489 */
490
491 $args['menu-item-url'] = '';
492
493 $original_title = '';
494
495 if ( 'taxonomy' === $args['menu-item-type'] ) {
496 $original_object = get_term( $args['menu-item-object-id'], $args['menu-item-object'] );
497
498 if ( $original_object instanceof WP_Term ) {
499 $original_parent = get_term_field( 'parent', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );
500 $original_title = get_term_field( 'name', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );
501 }
502 } elseif ( 'post_type' === $args['menu-item-type'] ) {
503 $original_object = get_post( $args['menu-item-object-id'] );
504
505 if ( $original_object instanceof WP_Post ) {
506 $original_parent = (int) $original_object->post_parent;
507 $original_title = $original_object->post_title;
508 }
509 } elseif ( 'post_type_archive' === $args['menu-item-type'] ) {
510 $original_object = get_post_type_object( $args['menu-item-object'] );
511
512 if ( $original_object instanceof WP_Post_Type ) {
513 $original_title = $original_object->labels->archives;
514 }
515 }
516
517 if ( wp_unslash( $args['menu-item-title'] ) === $original_title ) {
518 $args['menu-item-title'] = '';
519 }
520
521 // Hack to get wp to create a post object when too many properties are empty.
522 if ( '' === $args['menu-item-title'] && '' === $args['menu-item-description'] ) {
523 $args['menu-item-description'] = ' ';
524 }
525 }
526
527 // Populate the menu item object.
528 $post = array(
529 'menu_order' => $args['menu-item-position'],
530 'ping_status' => 0,
531 'post_content' => $args['menu-item-description'],
532 'post_excerpt' => $args['menu-item-attr-title'],
533 'post_parent' => $original_parent,
534 'post_title' => $args['menu-item-title'],
535 'post_type' => 'nav_menu_item',
536 );
537
538 $post_date = wp_resolve_post_date( $args['menu-item-post-date'], $args['menu-item-post-date-gmt'] );
539 if ( $post_date ) {
540 $post['post_date'] = $post_date;
541 }
542
543 $update = 0 !== $menu_item_db_id;
544
545 // New menu item. Default is draft status.
546 if ( ! $update ) {
547 $post['ID'] = 0;
548 $post['post_status'] = 'publish' === $args['menu-item-status'] ? 'publish' : 'draft';
549 $menu_item_db_id = wp_insert_post( $post, true, $fire_after_hooks );
550 if ( ! $menu_item_db_id || is_wp_error( $menu_item_db_id ) ) {
551 return $menu_item_db_id;
552 }
553
554 /**
555 * Fires immediately after a new navigation menu item has been added.
556 *
557 * @since 4.4.0
558 *
559 * @see wp_update_nav_menu_item()
560 *
561 * @param int $menu_id ID of the updated menu.
562 * @param int $menu_item_db_id ID of the new menu item.
563 * @param array $args An array of arguments used to update/add the menu item.
564 */
565 do_action( 'wp_add_nav_menu_item', $menu_id, $menu_item_db_id, $args );
566 }
567
568 /*
569 * Associate the menu item with the menu term.
570 * Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms().
571 */
572 if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) {
573 $update_terms = wp_set_object_terms( $menu_item_db_id, array( $menu->term_id ), 'nav_menu' );
574 if ( is_wp_error( $update_terms ) ) {
575 return $update_terms;
576 }
577 }
578
579 if ( 'custom' === $args['menu-item-type'] ) {
580 $args['menu-item-object-id'] = $menu_item_db_id;
581 $args['menu-item-object'] = 'custom';
582 }
583
584 $menu_item_db_id = (int) $menu_item_db_id;
585
586 // Reset invalid `menu_item_parent`.
587 if ( (int) $args['menu-item-parent-id'] === $menu_item_db_id ) {
588 $args['menu-item-parent-id'] = 0;
589 }
590
591 update_post_meta( $menu_item_db_id, '_menu_item_type', sanitize_key( $args['menu-item-type'] ) );
592 update_post_meta( $menu_item_db_id, '_menu_item_menu_item_parent', (string) ( (int) $args['menu-item-parent-id'] ) );
593 update_post_meta( $menu_item_db_id, '_menu_item_object_id', (string) ( (int) $args['menu-item-object-id'] ) );
594 update_post_meta( $menu_item_db_id, '_menu_item_object', sanitize_key( $args['menu-item-object'] ) );
595 update_post_meta( $menu_item_db_id, '_menu_item_target', sanitize_key( $args['menu-item-target'] ) );
596
597 $args['menu-item-classes'] = array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-classes'] ) );
598 $args['menu-item-xfn'] = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-xfn'] ) ) );
599 update_post_meta( $menu_item_db_id, '_menu_item_classes', $args['menu-item-classes'] );
600 update_post_meta( $menu_item_db_id, '_menu_item_xfn', $args['menu-item-xfn'] );
601 update_post_meta( $menu_item_db_id, '_menu_item_url', sanitize_url( $args['menu-item-url'] ) );
602
603 if ( 0 === $menu_id ) {
604 update_post_meta( $menu_item_db_id, '_menu_item_orphaned', (string) time() );
605 } elseif ( get_post_meta( $menu_item_db_id, '_menu_item_orphaned' ) ) {
606 delete_post_meta( $menu_item_db_id, '_menu_item_orphaned' );
607 }
608
609 // Update existing menu item. Default is publish status.
610 if ( $update ) {
611 $post['ID'] = $menu_item_db_id;
612 $post['post_status'] = ( 'draft' === $args['menu-item-status'] ) ? 'draft' : 'publish';
613
614 $update_post = wp_update_post( $post, true );
615 if ( is_wp_error( $update_post ) ) {
616 return $update_post;
617 }
618 }
619
620 /**
621 * Fires after a navigation menu item has been updated.
622 *
623 * @since 3.0.0
624 *
625 * @see wp_update_nav_menu_item()
626 *
627 * @param int $menu_id ID of the updated menu.
628 * @param int $menu_item_db_id ID of the updated menu item.
629 * @param array $args An array of arguments used to update a menu item.
630 */
631 do_action( 'wp_update_nav_menu_item', $menu_id, $menu_item_db_id, $args );
632
633 return $menu_item_db_id;
634}
635
636/**
637 * Returns all navigation menu objects.
638 *
639 * @since 3.0.0
640 * @since 4.1.0 Default value of the 'orderby' argument was changed from 'none'
641 * to 'name'.
642 *
643 * @param array $args Optional. Array of arguments passed on to get_terms().
644 * Default empty array.
645 * @return WP_Term[] An array of menu objects.
646 */
647function wp_get_nav_menus( $args = array() ) {
648 $defaults = array(
649 'taxonomy' => 'nav_menu',
650 'hide_empty' => false,
651 'orderby' => 'name',
652 );
653 $args = wp_parse_args( $args, $defaults );
654
655 /**
656 * Filters the navigation menu objects being returned.
657 *
658 * @since 3.0.0
659 *
660 * @see get_terms()
661 *
662 * @param WP_Term[] $menus An array of menu objects.
663 * @param array $args An array of arguments used to retrieve menu objects.
664 */
665 return apply_filters( 'wp_get_nav_menus', get_terms( $args ), $args );
666}
667
668/**
669 * Determines whether a menu item is valid.
670 *
671 * @link https://core.trac.wordpress.org/ticket/13958
672 *
673 * @since 3.2.0
674 * @access private
675 *
676 * @param object $item The menu item to check.
677 * @return bool False if invalid, otherwise true.
678 */
679function _is_valid_nav_menu_item( $item ) {
680 return empty( $item->_invalid );
681}
682
683/**
684 * Retrieves all menu items of a navigation menu.
685 *
686 * Note: Most arguments passed to the `$args` parameter – save for 'output_key' – are
687 * specifically for retrieving nav_menu_item posts from get_posts() and may only
688 * indirectly affect the ultimate ordering and content of the resulting nav menu
689 * items that get returned from this function.
690 *
691 * @since 3.0.0
692 *
693 * @param int|string|WP_Term $menu Menu ID, slug, name, or object.
694 * @param array $args {
695 * Optional. Arguments to pass to get_posts().
696 *
697 * @type string $order How to order nav menu items as queried with get_posts().
698 * Will be ignored if 'output' is ARRAY_A. Default 'ASC'.
699 * @type string $orderby Field to order menu items by as retrieved from get_posts().
700 * Supply an orderby field via 'output_key' to affect the
701 * output order of nav menu items. Default 'menu_order'.
702 * @type string $post_type Menu items post type. Default 'nav_menu_item'.
703 * @type string $post_status Menu items post status. Default 'publish'.
704 * @type string $output How to order outputted menu items. Default ARRAY_A.
705 * @type string $output_key Key to use for ordering the actual menu items that get
706 * returned. Note that that is not a get_posts() argument
707 * and will only affect output of menu items processed in
708 * this function. Default 'menu_order'.
709 * @type bool $nopaging Whether to retrieve all menu items (true) or paginate
710 * (false). Default true.
711 * @type bool $update_menu_item_cache Whether to update the menu item cache. Default true.
712 * }
713 * @return array|false Array of menu items, otherwise false.
714 */
715function wp_get_nav_menu_items( $menu, $args = array() ) {
716 $menu = wp_get_nav_menu_object( $menu );
717
718 if ( ! $menu ) {
719 return false;
720 }
721
722 if ( ! taxonomy_exists( 'nav_menu' ) ) {
723 return false;
724 }
725
726 $defaults = array(
727 'order' => 'ASC',
728 'orderby' => 'menu_order',
729 'post_type' => 'nav_menu_item',
730 'post_status' => 'publish',
731 'output' => ARRAY_A,
732 'output_key' => 'menu_order',
733 'nopaging' => true,
734 'update_menu_item_cache' => true,
735 'tax_query' => array(
736 array(
737 'taxonomy' => 'nav_menu',
738 'field' => 'term_taxonomy_id',
739 'terms' => $menu->term_taxonomy_id,
740 ),
741 ),
742 );
743 $args = wp_parse_args( $args, $defaults );
744 if ( $menu->count > 0 ) {
745 $items = get_posts( $args );
746 } else {
747 $items = array();
748 }
749
750 $items = array_map( 'wp_setup_nav_menu_item', $items );
751
752 if ( ! is_admin() ) { // Remove invalid items only on front end.
753 $items = array_filter( $items, '_is_valid_nav_menu_item' );
754 }
755
756 if ( ARRAY_A === $args['output'] ) {
757 $items = wp_list_sort(
758 $items,
759 array(
760 $args['output_key'] => 'ASC',
761 )
762 );
763
764 $i = 1;
765
766 foreach ( $items as $k => $item ) {
767 $items[ $k ]->{$args['output_key']} = $i++;
768 }
769 }
770
771 /**
772 * Filters the navigation menu items being returned.
773 *
774 * @since 3.0.0
775 *
776 * @param array $items An array of menu item post objects.
777 * @param object $menu The menu object.
778 * @param array $args An array of arguments used to retrieve menu item objects.
779 */
780 return apply_filters( 'wp_get_nav_menu_items', $items, $menu, $args );
781}
782
783/**
784 * Updates post and term caches for all linked objects for a list of menu items.
785 *
786 * @since 6.1.0
787 *
788 * @param WP_Post[] $menu_items Array of menu item post objects.
789 */
790function update_menu_item_cache( $menu_items ) {
791 $post_ids = array();
792 $term_ids = array();
793
794 foreach ( $menu_items as $menu_item ) {
795 if ( 'nav_menu_item' !== $menu_item->post_type ) {
796 continue;
797 }
798
799 $object_id = get_post_meta( $menu_item->ID, '_menu_item_object_id', true );
800 $type = get_post_meta( $menu_item->ID, '_menu_item_type', true );
801
802 if ( 'post_type' === $type ) {
803 $post_ids[] = (int) $object_id;
804 } elseif ( 'taxonomy' === $type ) {
805 $term_ids[] = (int) $object_id;
806 }
807 }
808
809 if ( ! empty( $post_ids ) ) {
810 _prime_post_caches( $post_ids, false );
811 }
812
813 if ( ! empty( $term_ids ) ) {
814 _prime_term_caches( $term_ids );
815 }
816}
817
818/**
819 * Decorates a menu item object with the shared navigation menu item properties.
820 *
821 * Properties:
822 * - ID: The term_id if the menu item represents a taxonomy term.
823 * - attr_title: The title attribute of the link element for this menu item.
824 * - classes: The array of class attribute values for the link element of this menu item.
825 * - db_id: The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist).
826 * - description: The description of this menu item.
827 * - menu_item_parent: The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise.
828 * - object: The type of object originally represented, such as 'category', 'post', or 'attachment'.
829 * - object_id: The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories.
830 * - post_parent: The DB ID of the original object's parent object, if any (0 otherwise).
831 * - post_title: A "no title" label if menu item represents a post that lacks a title.
832 * - target: The target attribute of the link element for this menu item.
833 * - title: The title of this menu item.
834 * - type: The family of objects originally represented, such as 'post_type' or 'taxonomy'.
835 * - type_label: The singular label used to describe this type of menu item.
836 * - url: The URL to which this menu item points.
837 * - xfn: The XFN relationship expressed in the link of this menu item.
838 * - _invalid: Whether the menu item represents an object that no longer exists.
839 *
840 * @since 3.0.0
841 *
842 * @param object $menu_item The menu item to modify.
843 * @return object The menu item with standard menu item properties.
844 */
845function wp_setup_nav_menu_item( $menu_item ) {
846
847 /**
848 * Filters whether to short-circuit the wp_setup_nav_menu_item() output.
849 *
850 * Returning a non-null value from the filter will short-circuit wp_setup_nav_menu_item(),
851 * returning that value instead.
852 *
853 * @since 6.3.0
854 *
855 * @param object|null $modified_menu_item Modified menu item. Default null.
856 * @param object $menu_item The menu item to modify.
857 */
858 $pre_menu_item = apply_filters( 'pre_wp_setup_nav_menu_item', null, $menu_item );
859
860 if ( null !== $pre_menu_item ) {
861 return $pre_menu_item;
862 }
863
864 if ( isset( $menu_item->post_type ) ) {
865 if ( 'nav_menu_item' === $menu_item->post_type ) {
866 $menu_item->db_id = (int) $menu_item->ID;
867 $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;
868 $menu_item->object_id = ! isset( $menu_item->object_id ) ? get_post_meta( $menu_item->ID, '_menu_item_object_id', true ) : $menu_item->object_id;
869 $menu_item->object = ! isset( $menu_item->object ) ? get_post_meta( $menu_item->ID, '_menu_item_object', true ) : $menu_item->object;
870 $menu_item->type = ! isset( $menu_item->type ) ? get_post_meta( $menu_item->ID, '_menu_item_type', true ) : $menu_item->type;
871
872 if ( 'post_type' === $menu_item->type ) {
873 $object = get_post_type_object( $menu_item->object );
874 if ( $object ) {
875 $menu_item->type_label = $object->labels->singular_name;
876 // Denote post states for special pages (only in the admin).
877 if ( function_exists( 'get_post_states' ) ) {
878 $menu_post = get_post( $menu_item->object_id );
879 $post_states = get_post_states( $menu_post );
880 if ( $post_states ) {
881 $menu_item->type_label = wp_strip_all_tags( implode( ', ', $post_states ) );
882 }
883 }
884 } else {
885 $menu_item->type_label = $menu_item->object;
886 $menu_item->_invalid = true;
887 }
888
889 if ( 'trash' === get_post_status( $menu_item->object_id ) ) {
890 $menu_item->_invalid = true;
891 }
892
893 $original_object = get_post( $menu_item->object_id );
894
895 if ( $original_object ) {
896 $menu_item->url = get_permalink( $original_object->ID );
897 /** This filter is documented in wp-includes/post-template.php */
898 $original_title = apply_filters( 'the_title', $original_object->post_title, $original_object->ID );
899 } else {
900 $menu_item->url = '';
901 $original_title = '';
902 $menu_item->_invalid = true;
903 }
904
905 if ( '' === $original_title ) {
906 /* translators: %d: ID of a post. */
907 $original_title = sprintf( __( '#%d (no title)' ), $menu_item->object_id );
908 }
909
910 $menu_item->title = ( '' === $menu_item->post_title ) ? $original_title : $menu_item->post_title;
911
912 } elseif ( 'post_type_archive' === $menu_item->type ) {
913 $object = get_post_type_object( $menu_item->object );
914 if ( $object ) {
915 $menu_item->title = ( '' === $menu_item->post_title ) ? $object->labels->archives : $menu_item->post_title;
916 $post_type_description = $object->description;
917 } else {
918 $post_type_description = '';
919 $menu_item->_invalid = true;
920 }
921
922 $menu_item->type_label = __( 'Post Type Archive' );
923 $post_content = wp_trim_words( $menu_item->post_content, 200 );
924 $post_type_description = ( '' === $post_content ) ? $post_type_description : $post_content;
925 $menu_item->url = get_post_type_archive_link( $menu_item->object );
926
927 } elseif ( 'taxonomy' === $menu_item->type ) {
928 $object = get_taxonomy( $menu_item->object );
929 if ( $object ) {
930 $menu_item->type_label = $object->labels->singular_name;
931 } else {
932 $menu_item->type_label = $menu_item->object;
933 $menu_item->_invalid = true;
934 }
935
936 $original_object = get_term( (int) $menu_item->object_id, $menu_item->object );
937
938 if ( $original_object && ! is_wp_error( $original_object ) ) {
939 $menu_item->url = get_term_link( (int) $menu_item->object_id, $menu_item->object );
940 $original_title = $original_object->name;
941 } else {
942 $menu_item->url = '';
943 $original_title = '';
944 $menu_item->_invalid = true;
945 }
946
947 if ( '' === $original_title ) {
948 /* translators: %d: ID of a term. */
949 $original_title = sprintf( __( '#%d (no title)' ), $menu_item->object_id );
950 }
951
952 $menu_item->title = ( '' === $menu_item->post_title ) ? $original_title : $menu_item->post_title;
953
954 } else {
955 $menu_item->type_label = __( 'Custom Link' );
956 $menu_item->title = $menu_item->post_title;
957 $menu_item->url = ! isset( $menu_item->url ) ? get_post_meta( $menu_item->ID, '_menu_item_url', true ) : $menu_item->url;
958 }
959
960 $menu_item->target = ! isset( $menu_item->target ) ? get_post_meta( $menu_item->ID, '_menu_item_target', true ) : $menu_item->target;
961
962 /**
963 * Filters a navigation menu item's title attribute.
964 *
965 * @since 3.0.0
966 *
967 * @param string $item_title The menu item title attribute.
968 */
969 $menu_item->attr_title = ! isset( $menu_item->attr_title ) ? apply_filters( 'nav_menu_attr_title', $menu_item->post_excerpt ) : $menu_item->attr_title;
970
971 if ( ! isset( $menu_item->description ) ) {
972 /**
973 * Filters a navigation menu item's description.
974 *
975 * @since 3.0.0
976 *
977 * @param string $description The menu item description.
978 */
979 $menu_item->description = apply_filters( 'nav_menu_description', wp_trim_words( $menu_item->post_content, 200 ) );
980 }
981
982 $menu_item->classes = ! isset( $menu_item->classes ) ? (array) get_post_meta( $menu_item->ID, '_menu_item_classes', true ) : $menu_item->classes;
983 $menu_item->xfn = ! isset( $menu_item->xfn ) ? get_post_meta( $menu_item->ID, '_menu_item_xfn', true ) : $menu_item->xfn;
984 } else {
985 $menu_item->db_id = 0;
986 $menu_item->menu_item_parent = 0;
987 $menu_item->object_id = (int) $menu_item->ID;
988 $menu_item->type = 'post_type';
989
990 $object = get_post_type_object( $menu_item->post_type );
991 $menu_item->object = $object->name;
992 $menu_item->type_label = $object->labels->singular_name;
993
994 if ( '' === $menu_item->post_title ) {
995 /* translators: %d: ID of a post. */
996 $menu_item->post_title = sprintf( __( '#%d (no title)' ), $menu_item->ID );
997 }
998
999 $menu_item->title = $menu_item->post_title;
1000 $menu_item->url = get_permalink( $menu_item->ID );
1001 $menu_item->target = '';
1002
1003 /** This filter is documented in wp-includes/nav-menu.php */
1004 $menu_item->attr_title = apply_filters( 'nav_menu_attr_title', '' );
1005
1006 /** This filter is documented in wp-includes/nav-menu.php */
1007 $menu_item->description = apply_filters( 'nav_menu_description', '' );
1008 $menu_item->classes = array();
1009 $menu_item->xfn = '';
1010 }
1011 } elseif ( isset( $menu_item->taxonomy ) ) {
1012 $menu_item->ID = $menu_item->term_id;
1013 $menu_item->db_id = 0;
1014 $menu_item->menu_item_parent = 0;
1015 $menu_item->object_id = (int) $menu_item->term_id;
1016 $menu_item->post_parent = (int) $menu_item->parent;
1017 $menu_item->type = 'taxonomy';
1018
1019 $object = get_taxonomy( $menu_item->taxonomy );
1020 $menu_item->object = $object->name;
1021 $menu_item->type_label = $object->labels->singular_name;
1022
1023 $menu_item->title = $menu_item->name;
1024 $menu_item->url = get_term_link( $menu_item, $menu_item->taxonomy );
1025 $menu_item->target = '';
1026 $menu_item->attr_title = '';
1027 $menu_item->description = get_term_field( 'description', $menu_item->term_id, $menu_item->taxonomy );
1028 $menu_item->classes = array();
1029 $menu_item->xfn = '';
1030
1031 }
1032
1033 /**
1034 * Filters a navigation menu item object.
1035 *
1036 * @since 3.0.0
1037 *
1038 * @param object $menu_item The menu item object.
1039 */
1040 return apply_filters( 'wp_setup_nav_menu_item', $menu_item );
1041}
1042
1043/**
1044 * Returns the menu items associated with a particular object.
1045 *
1046 * @since 3.0.0
1047 *
1048 * @param int $object_id Optional. The ID of the original object. Default 0.
1049 * @param string $object_type Optional. The type of object, such as 'post_type' or 'taxonomy'.
1050 * Default 'post_type'.
1051 * @param string $taxonomy Optional. If $object_type is 'taxonomy', $taxonomy is the name
1052 * of the tax that $object_id belongs to. Default empty.
1053 * @return int[] The array of menu item IDs; empty array if none.
1054 */
1055function wp_get_associated_nav_menu_items( $object_id = 0, $object_type = 'post_type', $taxonomy = '' ) {
1056 $object_id = (int) $object_id;
1057 $menu_item_ids = array();
1058
1059 $query = new WP_Query();
1060 $menu_items = $query->query(
1061 array(
1062 'meta_key' => '_menu_item_object_id',
1063 'meta_value' => $object_id,
1064 'post_status' => 'any',
1065 'post_type' => 'nav_menu_item',
1066 'posts_per_page' => -1,
1067 )
1068 );
1069 foreach ( (array) $menu_items as $menu_item ) {
1070 if ( isset( $menu_item->ID ) && is_nav_menu_item( $menu_item->ID ) ) {
1071 $menu_item_type = get_post_meta( $menu_item->ID, '_menu_item_type', true );
1072 if (
1073 'post_type' === $object_type &&
1074 'post_type' === $menu_item_type
1075 ) {
1076 $menu_item_ids[] = (int) $menu_item->ID;
1077 } elseif (
1078 'taxonomy' === $object_type &&
1079 'taxonomy' === $menu_item_type &&
1080 get_post_meta( $menu_item->ID, '_menu_item_object', true ) === $taxonomy
1081 ) {
1082 $menu_item_ids[] = (int) $menu_item->ID;
1083 }
1084 }
1085 }
1086
1087 return array_unique( $menu_item_ids );
1088}
1089
1090/**
1091 * Callback for handling a menu item when its original object is deleted.
1092 *
1093 * @since 3.0.0
1094 * @access private
1095 *
1096 * @param int $object_id The ID of the original object being trashed.
1097 */
1098function _wp_delete_post_menu_item( $object_id ) {
1099 $object_id = (int) $object_id;
1100
1101 $menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'post_type' );
1102
1103 foreach ( (array) $menu_item_ids as $menu_item_id ) {
1104 wp_delete_post( $menu_item_id, true );
1105 }
1106}
1107
1108/**
1109 * Serves as a callback for handling a menu item when its original object is deleted.
1110 *
1111 * @since 3.0.0
1112 * @access private
1113 *
1114 * @param int $object_id The ID of the original object being trashed.
1115 * @param int $tt_id Term taxonomy ID. Unused.
1116 * @param string $taxonomy Taxonomy slug.
1117 */
1118function _wp_delete_tax_menu_item( $object_id, $tt_id, $taxonomy ) {
1119 $object_id = (int) $object_id;
1120
1121 $menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'taxonomy', $taxonomy );
1122
1123 foreach ( (array) $menu_item_ids as $menu_item_id ) {
1124 wp_delete_post( $menu_item_id, true );
1125 }
1126}
1127
1128/**
1129 * Automatically add newly published page objects to menus with that as an option.
1130 *
1131 * @since 3.0.0
1132 * @access private
1133 *
1134 * @param string $new_status The new status of the post object.
1135 * @param string $old_status The old status of the post object.
1136 * @param WP_Post $post The post object being transitioned from one status to another.
1137 */
1138function _wp_auto_add_pages_to_menu( $new_status, $old_status, $post ) {
1139 if ( 'publish' !== $new_status || 'publish' === $old_status || 'page' !== $post->post_type ) {
1140 return;
1141 }
1142 if ( ! empty( $post->post_parent ) ) {
1143 return;
1144 }
1145 $auto_add = get_option( 'nav_menu_options' );
1146 if ( empty( $auto_add ) || ! is_array( $auto_add ) || ! isset( $auto_add['auto_add'] ) ) {
1147 return;
1148 }
1149 $auto_add = $auto_add['auto_add'];
1150 if ( empty( $auto_add ) || ! is_array( $auto_add ) ) {
1151 return;
1152 }
1153
1154 $args = array(
1155 'menu-item-object-id' => $post->ID,
1156 'menu-item-object' => $post->post_type,
1157 'menu-item-type' => 'post_type',
1158 'menu-item-status' => 'publish',
1159 );
1160
1161 foreach ( $auto_add as $menu_id ) {
1162 $items = wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) );
1163 if ( ! is_array( $items ) ) {
1164 continue;
1165 }
1166 foreach ( $items as $item ) {
1167 if ( $post->ID === (int) $item->object_id ) {
1168 continue 2;
1169 }
1170 }
1171 wp_update_nav_menu_item( $menu_id, 0, $args );
1172 }
1173}
1174
1175/**
1176 * Deletes auto-draft posts associated with the supplied changeset.
1177 *
1178 * @since 4.8.0
1179 * @access private
1180 *
1181 * @param int $post_id Post ID for the customize_changeset.
1182 */
1183function _wp_delete_customize_changeset_dependent_auto_drafts( $post_id ) {
1184 $post = get_post( $post_id );
1185
1186 if ( ! $post || 'customize_changeset' !== $post->post_type ) {
1187 return;
1188 }
1189
1190 $data = json_decode( $post->post_content, true );
1191 if ( empty( $data['nav_menus_created_posts']['value'] ) ) {
1192 return;
1193 }
1194 remove_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
1195 foreach ( $data['nav_menus_created_posts']['value'] as $stub_post_id ) {
1196 if ( empty( $stub_post_id ) ) {
1197 continue;
1198 }
1199 if ( 'auto-draft' === get_post_status( $stub_post_id ) ) {
1200 wp_delete_post( $stub_post_id, true );
1201 } elseif ( 'draft' === get_post_status( $stub_post_id ) ) {
1202 wp_trash_post( $stub_post_id );
1203 delete_post_meta( $stub_post_id, '_customize_changeset_uuid' );
1204 }
1205 }
1206 add_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
1207}
1208
1209/**
1210 * Handles menu config after theme change.
1211 *
1212 * @access private
1213 * @since 4.9.0
1214 */
1215function _wp_menus_changed() {
1216 $old_nav_menu_locations = get_option( 'theme_switch_menu_locations', array() );
1217 $new_nav_menu_locations = get_nav_menu_locations();
1218 $mapped_nav_menu_locations = wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations );
1219
1220 set_theme_mod( 'nav_menu_locations', $mapped_nav_menu_locations );
1221 delete_option( 'theme_switch_menu_locations' );
1222}
1223
1224/**
1225 * Maps nav menu locations according to assignments in previously active theme.
1226 *
1227 * @since 4.9.0
1228 *
1229 * @param array $new_nav_menu_locations New nav menu locations assignments.
1230 * @param array $old_nav_menu_locations Old nav menu locations assignments.
1231 * @return array Nav menus mapped to new nav menu locations.
1232 */
1233function wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations ) {
1234 $registered_nav_menus = get_registered_nav_menus();
1235 $new_nav_menu_locations = array_intersect_key( $new_nav_menu_locations, $registered_nav_menus );
1236
1237 // Short-circuit if there are no old nav menu location assignments to map.
1238 if ( empty( $old_nav_menu_locations ) ) {
1239 return $new_nav_menu_locations;
1240 }
1241
1242 // If old and new theme have just one location, map it and we're done.
1243 if ( 1 === count( $old_nav_menu_locations ) && 1 === count( $registered_nav_menus ) ) {
1244 $new_nav_menu_locations[ key( $registered_nav_menus ) ] = array_pop( $old_nav_menu_locations );
1245 return $new_nav_menu_locations;
1246 }
1247
1248 $old_locations = array_keys( $old_nav_menu_locations );
1249
1250 // Map locations with the same slug.
1251 foreach ( $registered_nav_menus as $location => $name ) {
1252 if ( in_array( $location, $old_locations, true ) ) {
1253 $new_nav_menu_locations[ $location ] = $old_nav_menu_locations[ $location ];
1254 unset( $old_nav_menu_locations[ $location ] );
1255 }
1256 }
1257
1258 // If there are no old nav menu locations left, then we're done.
1259 if ( empty( $old_nav_menu_locations ) ) {
1260 return $new_nav_menu_locations;
1261 }
1262
1263 /*
1264 * If old and new theme both have locations that contain phrases
1265 * from within the same group, make an educated guess and map it.
1266 */
1267 $common_slug_groups = array(
1268 array( 'primary', 'menu-1', 'main', 'header', 'navigation', 'top' ),
1269 array( 'secondary', 'menu-2', 'footer', 'subsidiary', 'bottom' ),
1270 array( 'social' ),
1271 );
1272
1273 // Go through each group...
1274 foreach ( $common_slug_groups as $slug_group ) {
1275
1276 // ...and see if any of these slugs...
1277 foreach ( $slug_group as $slug ) {
1278
1279 // ...and any of the new menu locations...
1280 foreach ( $registered_nav_menus as $new_location => $name ) {
1281
1282 // ...actually match!
1283 if ( is_string( $new_location ) && false === stripos( $new_location, $slug ) && false === stripos( $slug, $new_location ) ) {
1284 continue;
1285 } elseif ( is_numeric( $new_location ) && $new_location !== $slug ) {
1286 continue;
1287 }
1288
1289 // Then see if any of the old locations...
1290 foreach ( $old_nav_menu_locations as $location => $menu_id ) {
1291
1292 // ...and any slug in the same group...
1293 foreach ( $slug_group as $slug ) {
1294
1295 // ... have a match as well.
1296 if ( is_string( $location ) && false === stripos( $location, $slug ) && false === stripos( $slug, $location ) ) {
1297 continue;
1298 } elseif ( is_numeric( $location ) && $location !== $slug ) {
1299 continue;
1300 }
1301
1302 // Make sure this location wasn't mapped and removed previously.
1303 if ( ! empty( $old_nav_menu_locations[ $location ] ) ) {
1304
1305 // We have a match that can be mapped!
1306 $new_nav_menu_locations[ $new_location ] = $old_nav_menu_locations[ $location ];
1307
1308 // Remove the mapped location so it can't be mapped again.
1309 unset( $old_nav_menu_locations[ $location ] );
1310
1311 // Go back and check the next new menu location.
1312 continue 3;
1313 }
1314 } // End foreach ( $slug_group as $slug ).
1315 } // End foreach ( $old_nav_menu_locations as $location => $menu_id ).
1316 } // End foreach foreach ( $registered_nav_menus as $new_location => $name ).
1317 } // End foreach ( $slug_group as $slug ).
1318 } // End foreach ( $common_slug_groups as $slug_group ).
1319
1320 return $new_nav_menu_locations;
1321}
1322
1323/**
1324 * Prevents menu items from being their own parent.
1325 *
1326 * Resets menu_item_parent to 0 when the parent is set to the item itself.
1327 * For use before saving `_menu_item_menu_item_parent` in nav-menus.php.
1328 *
1329 * @since 6.2.0
1330 * @access private
1331 *
1332 * @param array $menu_item_data The menu item data array.
1333 * @return array The menu item data with reset menu_item_parent.
1334 */
1335function _wp_reset_invalid_menu_item_parent( $menu_item_data ) {
1336 if ( ! is_array( $menu_item_data ) ) {
1337 return $menu_item_data;
1338 }
1339
1340 if (
1341 ! empty( $menu_item_data['ID'] ) &&
1342 ! empty( $menu_item_data['menu_item_parent'] ) &&
1343 (int) $menu_item_data['ID'] === (int) $menu_item_data['menu_item_parent']
1344 ) {
1345 $menu_item_data['menu_item_parent'] = 0;
1346 }
1347
1348 return $menu_item_data;
1349}
1350