run: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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
2.35 KB
2026-03-11 16:18:52
R W Run
1.12 KB
2026-03-11 16:18:52
R W Run
4.31 KB
2026-03-11 16:18:52
R W Run
5.61 KB
2026-03-11 16:18:51
R W Run
4.09 KB
2026-03-11 16:18:52
R W Run
213.43 KB
2026-03-11 16:18:51
R W Run
1.71 KB
2026-03-11 16:18:51
R W Run
5.93 KB
2026-03-11 16:18:51
R W Run
4.54 KB
2026-03-11 16:18:52
R W Run
2.08 KB
2026-03-11 16:18:52
R W Run
2.4 KB
2026-03-11 16:18:52
R W Run
1.82 KB
2026-03-11 16:18:52
R W Run
1.67 KB
2026-03-11 16:18:52
R W Run
2.03 KB
2026-03-11 16:18:51
R W Run
4.39 KB
2026-03-11 16:18:52
R W Run
1.88 KB
2026-03-11 16:18:51
R W Run
1.59 KB
2026-03-11 16:18:51
R W Run
1.75 KB
2026-03-11 16:18:51
R W Run
1.23 KB
2026-03-11 16:18:52
R W Run
2.71 KB
2026-03-11 16:18:51
R W Run
6.63 KB
2026-03-11 16:18:52
R W Run
3.1 KB
2026-03-11 16:18:52
R W Run
1.83 KB
2026-03-11 16:18:52
R W Run
3.68 KB
2026-03-11 16:18:51
R W Run
6.29 KB
2026-03-11 16:18:52
R W Run
1.27 KB
2026-03-11 16:18:52
R W Run
5.31 KB
2026-03-11 16:18:52
R W Run
13.5 KB
2026-03-11 16:18:52
R W Run
4.99 KB
2026-03-11 16:18:52
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
8.53 KB
2026-03-11 16:18:51
R W Run
3.92 KB
2026-03-11 16:18:51
R W Run
1.24 KB
2026-03-11 16:18:51
R W Run
1.63 KB
2026-03-11 16:18:51
R W Run
4.28 KB
2026-03-11 16:18:51
R W Run
13.63 KB
2026-03-11 16:18:51
R W Run
9.74 KB
2026-03-11 16:18:52
R W Run
48.39 KB
2026-03-11 16:18:52
R W Run
361 By
2026-03-11 16:18:52
R W Run
13.29 KB
2026-03-11 16:18:51
R W Run
1.75 KB
2026-03-11 16:18:52
R W Run
1.49 KB
2026-03-11 16:18:51
R W Run
1.91 KB
2026-03-11 16:18:52
R W Run
2.67 KB
2026-03-11 16:18:51
R W Run
1.25 KB
2026-03-11 16:18:52
R W Run
2.74 KB
2026-03-11 16:18:52
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
2.29 KB
2026-03-11 16:18:52
R W Run
3.55 KB
2026-03-11 16:18:51
R W Run
3.53 KB
2026-03-11 16:18:51
R W Run
9.14 KB
2026-03-11 16:18:52
R W Run
4.72 KB
2026-03-11 16:18:51
R W Run
5.61 KB
2026-03-11 16:18:52
R W Run
3.55 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:52
R W Run
2.09 KB
2026-03-11 16:18:51
R W Run
1.8 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.66 KB
2026-03-11 16:18:52
R W Run
3.49 KB
2026-03-11 16:18:52
R W Run
1.15 KB
2026-03-11 16:18:51
R W Run
2.87 KB
2026-03-11 16:18:51
R W Run
2.48 KB
2026-03-11 16:18:52
R W Run
5.56 KB
2026-03-11 16:18:51
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
4.59 KB
2026-03-11 16:18:51
R W Run
558 By
2026-03-11 16:18:52
R W Run
4.5 KB
2026-03-11 16:18:52
R W Run
23.26 KB
2026-03-11 16:18:52
R W Run
735 By
2026-03-11 16:18:52
R W Run
6.19 KB
2026-03-11 16:18:51
R W Run
1.17 KB
2026-03-11 16:18:51
R W Run
1.82 KB
2026-03-11 16:18:52
R W Run
66.15 KB
2026-03-11 16:18:52
R W Run
1.55 KB
2026-03-11 16:18:51
R W Run
9.92 KB
2026-03-11 16:18:52
R W Run
1.8 KB
2026-03-11 16:18:52
R W Run
1.73 KB
2026-03-11 16:18:52
R W Run
2.02 KB
2026-03-11 16:18:52
R W Run
4.41 KB
2026-03-11 16:18:52
R W Run
2.67 KB
2026-03-11 16:18:51
R W Run
2.38 KB
2026-03-11 16:18:52
R W Run
error_log
📄navigation.php
1<?php
2/**
3 * Server-side rendering of the `core/navigation` block.
4 *
5 * @package WordPress
6 */
7
8/**
9 * Helper functions used to render the navigation block.
10 *
11 * @since 6.5.0
12 */
13class WP_Navigation_Block_Renderer {
14
15 /**
16 * Used to determine whether or not a navigation has submenus.
17 *
18 * @since 6.5.0
19 */
20 private static $has_submenus = false;
21
22 /**
23 * Used to determine which blocks need an <li> wrapper.
24 *
25 * @since 6.5.0
26 *
27 * @var array
28 */
29 private static $needs_list_item_wrapper = array(
30 'core/site-title',
31 'core/site-logo',
32 'core/social-links',
33 );
34
35 /**
36 * Keeps track of all the navigation names that have been seen.
37 *
38 * @since 6.5.0
39 *
40 * @var array
41 */
42 private static $seen_menu_names = array();
43
44 /**
45 * Returns whether or not this is responsive navigation.
46 *
47 * @since 6.5.0
48 *
49 * @param array $attributes The block attributes.
50 * @return bool Returns whether or not this is responsive navigation.
51 */
52 private static function is_responsive( $attributes ) {
53 /**
54 * This is for backwards compatibility after the `isResponsive` attribute was been removed.
55 */
56
57 $has_old_responsive_attribute = ! empty( $attributes['isResponsive'] ) && $attributes['isResponsive'];
58 return isset( $attributes['overlayMenu'] ) && 'never' !== $attributes['overlayMenu'] || $has_old_responsive_attribute;
59 }
60
61 /**
62 * Returns whether or not a navigation has a submenu.
63 *
64 * @since 6.5.0
65 *
66 * @param WP_Block_List $inner_blocks The list of inner blocks.
67 * @return bool Returns whether or not a navigation has a submenu and also sets the member variable.
68 */
69 private static function has_submenus( $inner_blocks ) {
70 if ( true === static::$has_submenus ) {
71 return static::$has_submenus;
72 }
73
74 foreach ( $inner_blocks as $inner_block ) {
75 // If this is a page list then work out if any of the pages have children.
76 if ( 'core/page-list' === $inner_block->name ) {
77 $all_pages = get_pages(
78 array(
79 'sort_column' => 'menu_order,post_title',
80 'order' => 'asc',
81 )
82 );
83 foreach ( (array) $all_pages as $page ) {
84 if ( $page->post_parent ) {
85 static::$has_submenus = true;
86 break;
87 }
88 }
89 }
90 // If this is a navigation submenu then we know we have submenus.
91 if ( 'core/navigation-submenu' === $inner_block->name ) {
92 static::$has_submenus = true;
93 break;
94 }
95 }
96
97 return static::$has_submenus;
98 }
99
100 /**
101 * Determine whether the navigation blocks is interactive.
102 *
103 * @since 6.5.0
104 *
105 * @param array $attributes The block attributes.
106 * @param WP_Block_List $inner_blocks The list of inner blocks.
107 * @return bool Returns whether or not to load the view script.
108 */
109 private static function is_interactive( $attributes, $inner_blocks ) {
110 $has_submenus = static::has_submenus( $inner_blocks );
111 $is_responsive_menu = static::is_responsive( $attributes );
112 return ( $has_submenus && ( $attributes['openSubmenusOnClick'] || $attributes['showSubmenuIcon'] ) ) || $is_responsive_menu;
113 }
114
115 /**
116 * Returns whether or not a block needs a list item wrapper.
117 *
118 * @since 6.5.0
119 *
120 * @param WP_Block $block The block.
121 * @return bool Returns whether or not a block needs a list item wrapper.
122 */
123 private static function does_block_need_a_list_item_wrapper( $block ) {
124
125 /**
126 * Filter the list of blocks that need a list item wrapper.
127 *
128 * Affords the ability to customize which blocks need a list item wrapper when rendered
129 * within a core/navigation block.
130 * This is useful for blocks that are not list items but should be wrapped in a list
131 * item when used as a child of a navigation block.
132 *
133 * @since 6.5.0
134 *
135 * @param array $needs_list_item_wrapper The list of blocks that need a list item wrapper.
136 */
137 $needs_list_item_wrapper = apply_filters( 'block_core_navigation_listable_blocks', static::$needs_list_item_wrapper );
138
139 return in_array( $block->name, $needs_list_item_wrapper, true );
140 }
141
142 /**
143 * Returns the markup for a single inner block.
144 *
145 * @since 6.5.0
146 *
147 * @param WP_Block $inner_block The inner block.
148 * @return string Returns the markup for a single inner block.
149 */
150 private static function get_markup_for_inner_block( $inner_block ) {
151 $inner_block_content = $inner_block->render();
152 if ( ! empty( $inner_block_content ) ) {
153 if ( static::does_block_need_a_list_item_wrapper( $inner_block ) ) {
154 return '<li class="wp-block-navigation-item">' . $inner_block_content . '</li>';
155 }
156 }
157
158 return $inner_block_content;
159 }
160
161 /**
162 * Returns the html for the inner blocks of the navigation block.
163 *
164 * @since 6.5.0
165 *
166 * @param array $attributes The block attributes.
167 * @param WP_Block_List $inner_blocks The list of inner blocks.
168 * @return string Returns the html for the inner blocks of the navigation block.
169 */
170 private static function get_inner_blocks_html( $attributes, $inner_blocks ) {
171 $has_submenus = static::has_submenus( $inner_blocks );
172 $is_interactive = static::is_interactive( $attributes, $inner_blocks );
173
174 $style = static::get_styles( $attributes );
175 $class = static::get_classes( $attributes );
176 $container_attributes = get_block_wrapper_attributes(
177 array(
178 'class' => 'wp-block-navigation__container ' . $class,
179 'style' => $style,
180 )
181 );
182
183 $inner_blocks_html = '';
184 $is_list_open = false;
185
186 foreach ( $inner_blocks as $inner_block ) {
187 $inner_block_markup = static::get_markup_for_inner_block( $inner_block );
188 $p = new WP_HTML_Tag_Processor( $inner_block_markup );
189 $is_list_item = $p->next_tag( 'LI' );
190
191 if ( $is_list_item && ! $is_list_open ) {
192 $is_list_open = true;
193 $inner_blocks_html .= sprintf(
194 '<ul %1$s>',
195 $container_attributes
196 );
197 }
198
199 if ( ! $is_list_item && $is_list_open ) {
200 $is_list_open = false;
201 $inner_blocks_html .= '</ul>';
202 }
203
204 $inner_blocks_html .= $inner_block_markup;
205 }
206
207 if ( $is_list_open ) {
208 $inner_blocks_html .= '</ul>';
209 }
210
211 // Add directives to the submenu if needed.
212 if ( $has_submenus && $is_interactive ) {
213 $tags = new WP_HTML_Tag_Processor( $inner_blocks_html );
214 $inner_blocks_html = block_core_navigation_add_directives_to_submenu( $tags, $attributes );
215 }
216
217 return $inner_blocks_html;
218 }
219
220 /**
221 * Gets the inner blocks for the navigation block from the navigation post.
222 *
223 * @since 6.5.0
224 *
225 * @param array $attributes The block attributes.
226 * @return WP_Block_List Returns the inner blocks for the navigation block.
227 */
228 private static function get_inner_blocks_from_navigation_post( $attributes ) {
229 $navigation_post = get_post( $attributes['ref'] );
230 if ( ! isset( $navigation_post ) ) {
231 return new WP_Block_List( array(), $attributes );
232 }
233
234 // Only published posts are valid. If this is changed then a corresponding change
235 // must also be implemented in `use-navigation-menu.js`.
236 if ( 'publish' === $navigation_post->post_status ) {
237 $parsed_blocks = parse_blocks( $navigation_post->post_content );
238
239 // 'parse_blocks' includes a null block with '\n\n' as the content when
240 // it encounters whitespace. This code strips it.
241 $blocks = block_core_navigation_filter_out_empty_blocks( $parsed_blocks );
242
243 // Re-serialize, and run Block Hooks algorithm to inject hooked blocks.
244 // TODO: See if we can move the apply_block_hooks_to_content_from_post_object() call
245 // before the parse_blocks() call further above, to avoid the extra serialization/parsing.
246 $markup = serialize_blocks( $blocks );
247 $markup = apply_block_hooks_to_content_from_post_object( $markup, $navigation_post );
248 $blocks = parse_blocks( $markup );
249
250 // TODO - this uses the full navigation block attributes for the
251 // context which could be refined.
252 return new WP_Block_List( $blocks, $attributes );
253 }
254 }
255
256 /**
257 * Gets the inner blocks for the navigation block from the fallback.
258 *
259 * @since 6.5.0
260 *
261 * @param array $attributes The block attributes.
262 * @return WP_Block_List Returns the inner blocks for the navigation block.
263 */
264 private static function get_inner_blocks_from_fallback( $attributes ) {
265 $fallback_blocks = block_core_navigation_get_fallback_blocks();
266
267 // Fallback my have been filtered so do basic test for validity.
268 if ( empty( $fallback_blocks ) || ! is_array( $fallback_blocks ) ) {
269 return new WP_Block_List( array(), $attributes );
270 }
271
272 return new WP_Block_List( $fallback_blocks, $attributes );
273 }
274
275 /**
276 * Gets the inner blocks for the navigation block.
277 *
278 * @since 6.5.0
279 *
280 * @param array $attributes The block attributes.
281 * @param WP_Block $block The parsed block.
282 * @return WP_Block_List Returns the inner blocks for the navigation block.
283 */
284 private static function get_inner_blocks( $attributes, $block ) {
285 $inner_blocks = $block->inner_blocks;
286
287 // Ensure that blocks saved with the legacy ref attribute name (navigationMenuId) continue to render.
288 if ( array_key_exists( 'navigationMenuId', $attributes ) ) {
289 $attributes['ref'] = $attributes['navigationMenuId'];
290 }
291
292 // If:
293 // - the gutenberg plugin is active
294 // - `__unstableLocation` is defined
295 // - we have menu items at the defined location
296 // - we don't have a relationship to a `wp_navigation` Post (via `ref`).
297 // ...then create inner blocks from the classic menu assigned to that location.
298 if (
299 defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN &&
300 array_key_exists( '__unstableLocation', $attributes ) &&
301 ! array_key_exists( 'ref', $attributes ) &&
302 ! empty( block_core_navigation_get_menu_items_at_location( $attributes['__unstableLocation'] ) )
303 ) {
304 $inner_blocks = block_core_navigation_get_inner_blocks_from_unstable_location( $attributes );
305 }
306
307 // Load inner blocks from the navigation post.
308 if ( array_key_exists( 'ref', $attributes ) ) {
309 $inner_blocks = static::get_inner_blocks_from_navigation_post( $attributes );
310 }
311
312 // If there are no inner blocks then fallback to rendering an appropriate fallback.
313 if ( empty( $inner_blocks ) ) {
314 $inner_blocks = static::get_inner_blocks_from_fallback( $attributes );
315 }
316
317 /**
318 * Filter navigation block $inner_blocks.
319 * Allows modification of a navigation block menu items.
320 *
321 * @since 6.1.0
322 *
323 * @param \WP_Block_List $inner_blocks
324 */
325 $inner_blocks = apply_filters( 'block_core_navigation_render_inner_blocks', $inner_blocks );
326
327 $post_ids = block_core_navigation_get_post_ids( $inner_blocks );
328 if ( $post_ids ) {
329 _prime_post_caches( $post_ids, false, false );
330 }
331
332 return $inner_blocks;
333 }
334
335 /**
336 * Gets the name of the current navigation, if it has one.
337 *
338 * @since 6.5.0
339 *
340 * @param array $attributes The block attributes.
341 * @return string Returns the name of the navigation.
342 */
343 private static function get_navigation_name( $attributes ) {
344
345 $navigation_name = $attributes['ariaLabel'] ?? '';
346
347 if ( ! empty( $navigation_name ) ) {
348 return $navigation_name;
349 }
350
351 // Load the navigation post.
352 if ( array_key_exists( 'ref', $attributes ) ) {
353 $navigation_post = get_post( $attributes['ref'] );
354 if ( ! isset( $navigation_post ) ) {
355 return $navigation_name;
356 }
357
358 // Only published posts are valid. If this is changed then a corresponding change
359 // must also be implemented in `use-navigation-menu.js`.
360 if ( 'publish' === $navigation_post->post_status ) {
361 $navigation_name = $navigation_post->post_title;
362
363 // This is used to count the number of times a navigation name has been seen,
364 // so that we can ensure every navigation has a unique id.
365 if ( isset( static::$seen_menu_names[ $navigation_name ] ) ) {
366 ++static::$seen_menu_names[ $navigation_name ];
367 } else {
368 static::$seen_menu_names[ $navigation_name ] = 1;
369 }
370 }
371 }
372
373 return $navigation_name;
374 }
375
376 /**
377 * Returns the layout class for the navigation block.
378 *
379 * @since 6.5.0
380 *
381 * @param array $attributes The block attributes.
382 * @return string Returns the layout class for the navigation block.
383 */
384 private static function get_layout_class( $attributes ) {
385 $layout_justification = array(
386 'left' => 'items-justified-left',
387 'right' => 'items-justified-right',
388 'center' => 'items-justified-center',
389 'space-between' => 'items-justified-space-between',
390 );
391
392 $layout_class = '';
393 if (
394 isset( $attributes['layout']['justifyContent'] ) &&
395 isset( $layout_justification[ $attributes['layout']['justifyContent'] ] )
396 ) {
397 $layout_class .= $layout_justification[ $attributes['layout']['justifyContent'] ];
398 }
399 if ( isset( $attributes['layout']['orientation'] ) && 'vertical' === $attributes['layout']['orientation'] ) {
400 $layout_class .= ' is-vertical';
401 }
402
403 if ( isset( $attributes['layout']['flexWrap'] ) && 'nowrap' === $attributes['layout']['flexWrap'] ) {
404 $layout_class .= ' no-wrap';
405 }
406 return $layout_class;
407 }
408
409 /**
410 * Return classes for the navigation block.
411 *
412 * @since 6.5.0
413 *
414 * @param array $attributes The block attributes.
415 * @return string Returns the classes for the navigation block.
416 */
417 private static function get_classes( $attributes ) {
418 // Restore legacy classnames for submenu positioning.
419 $layout_class = static::get_layout_class( $attributes );
420 $colors = block_core_navigation_build_css_colors( $attributes );
421 $font_sizes = block_core_navigation_build_css_font_sizes( $attributes );
422 $is_responsive_menu = static::is_responsive( $attributes );
423
424 // Manually add block support text decoration as CSS class.
425 $text_decoration = $attributes['style']['typography']['textDecoration'] ?? null;
426 $text_decoration_class = sprintf( 'has-text-decoration-%s', $text_decoration );
427
428 $classes = array_merge(
429 $colors['css_classes'],
430 $font_sizes['css_classes'],
431 $is_responsive_menu ? array( 'is-responsive' ) : array(),
432 $layout_class ? array( $layout_class ) : array(),
433 $text_decoration ? array( $text_decoration_class ) : array()
434 );
435 return implode( ' ', $classes );
436 }
437
438 /**
439 * Get styles for the navigation block.
440 *
441 * @since 6.5.0
442 *
443 * @param array $attributes The block attributes.
444 * @return string Returns the styles for the navigation block.
445 */
446 private static function get_styles( $attributes ) {
447 $colors = block_core_navigation_build_css_colors( $attributes );
448 $font_sizes = block_core_navigation_build_css_font_sizes( $attributes );
449 $block_styles = isset( $attributes['styles'] ) ? $attributes['styles'] : '';
450 return $block_styles . $colors['inline_styles'] . $font_sizes['inline_styles'];
451 }
452
453 /**
454 * Get the responsive container markup
455 *
456 * @since 6.5.0
457 *
458 * @param array $attributes The block attributes.
459 * @param WP_Block_List $inner_blocks The list of inner blocks.
460 * @param string $inner_blocks_html The markup for the inner blocks.
461 * @return string Returns the container markup.
462 */
463 private static function get_responsive_container_markup( $attributes, $inner_blocks, $inner_blocks_html ) {
464 $is_interactive = static::is_interactive( $attributes, $inner_blocks );
465 $colors = block_core_navigation_build_css_colors( $attributes );
466 $modal_unique_id = wp_unique_id( 'modal-' );
467
468 $is_hidden_by_default = isset( $attributes['overlayMenu'] ) && 'always' === $attributes['overlayMenu'];
469
470 $responsive_container_classes = array(
471 'wp-block-navigation__responsive-container',
472 $is_hidden_by_default ? 'hidden-by-default' : '',
473 implode( ' ', $colors['overlay_css_classes'] ),
474 );
475 $open_button_classes = array(
476 'wp-block-navigation__responsive-container-open',
477 $is_hidden_by_default ? 'always-shown' : '',
478 );
479
480 $should_display_icon_label = isset( $attributes['hasIcon'] ) && true === $attributes['hasIcon'];
481 $toggle_button_icon = '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 7.5h16v1.5H4z"></path><path d="M4 15h16v1.5H4z"></path></svg>';
482 if ( isset( $attributes['icon'] ) ) {
483 if ( 'menu' === $attributes['icon'] ) {
484 $toggle_button_icon = '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 5v1.5h14V5H5z"></path><path d="M5 12.8h14v-1.5H5v1.5z"></path><path d="M5 19h14v-1.5H5V19z"></path></svg>';
485 }
486 }
487 $toggle_button_content = $should_display_icon_label ? $toggle_button_icon : __( 'Menu' );
488 $toggle_close_button_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><path d="m13.06 12 6.47-6.47-1.06-1.06L12 10.94 5.53 4.47 4.47 5.53 10.94 12l-6.47 6.47 1.06 1.06L12 13.06l6.47 6.47 1.06-1.06L13.06 12Z"></path></svg>';
489 $toggle_close_button_content = $should_display_icon_label ? $toggle_close_button_icon : __( 'Close' );
490 $toggle_aria_label_open = $should_display_icon_label ? 'aria-label="' . __( 'Open menu' ) . '"' : ''; // Open button label.
491 $toggle_aria_label_close = $should_display_icon_label ? 'aria-label="' . __( 'Close menu' ) . '"' : ''; // Close button label.
492
493 // Add Interactivity API directives to the markup if needed.
494 $open_button_directives = '';
495 $responsive_container_directives = '';
496 $responsive_dialog_directives = '';
497 $close_button_directives = '';
498 if ( $is_interactive ) {
499 $open_button_directives = '
500 data-wp-on--click="actions.openMenuOnClick"
501 data-wp-on--keydown="actions.handleMenuKeydown"
502 ';
503 $responsive_container_directives = '
504 data-wp-class--has-modal-open="state.isMenuOpen"
505 data-wp-class--is-menu-open="state.isMenuOpen"
506 data-wp-watch="callbacks.initMenu"
507 data-wp-on--keydown="actions.handleMenuKeydown"
508 data-wp-on--focusout="actions.handleMenuFocusout"
509 tabindex="-1"
510 ';
511 $responsive_dialog_directives = '
512 data-wp-bind--aria-modal="state.ariaModal"
513 data-wp-bind--aria-label="state.ariaLabel"
514 data-wp-bind--role="state.roleAttribute"
515 ';
516 $close_button_directives = '
517 data-wp-on--click="actions.closeMenuOnClick"
518 ';
519 $responsive_container_content_directives = '
520 data-wp-watch="callbacks.focusFirstElement"
521 ';
522 }
523
524 $overlay_inline_styles = esc_attr( safecss_filter_attr( $colors['overlay_inline_styles'] ) );
525
526 return sprintf(
527 '<button aria-haspopup="dialog" %3$s class="%6$s" %10$s>%8$s</button>
528 <div class="%5$s" %7$s id="%1$s" %11$s>
529 <div class="wp-block-navigation__responsive-close" tabindex="-1">
530 <div class="wp-block-navigation__responsive-dialog" %12$s>
531 <button %4$s class="wp-block-navigation__responsive-container-close" %13$s>%9$s</button>
532 <div class="wp-block-navigation__responsive-container-content" %14$s id="%1$s-content">
533 %2$s
534 </div>
535 </div>
536 </div>
537 </div>',
538 esc_attr( $modal_unique_id ),
539 $inner_blocks_html,
540 $toggle_aria_label_open,
541 $toggle_aria_label_close,
542 esc_attr( trim( implode( ' ', $responsive_container_classes ) ) ),
543 esc_attr( trim( implode( ' ', $open_button_classes ) ) ),
544 ( ! empty( $overlay_inline_styles ) ) ? "style=\"$overlay_inline_styles\"" : '',
545 $toggle_button_content,
546 $toggle_close_button_content,
547 $open_button_directives,
548 $responsive_container_directives,
549 $responsive_dialog_directives,
550 $close_button_directives,
551 $responsive_container_content_directives
552 );
553 }
554
555 /**
556 * Get the wrapper attributes
557 *
558 * @since 6.5.0
559 *
560 * @param array $attributes The block attributes.
561 * @param WP_Block_List $inner_blocks A list of inner blocks.
562 * @return string Returns the navigation block markup.
563 */
564 private static function get_nav_wrapper_attributes( $attributes, $inner_blocks ) {
565 $nav_menu_name = static::get_unique_navigation_name( $attributes );
566 $is_interactive = static::is_interactive( $attributes, $inner_blocks );
567 $is_responsive_menu = static::is_responsive( $attributes );
568 $style = static::get_styles( $attributes );
569 $class = static::get_classes( $attributes );
570 $extra_attributes = array(
571 'class' => $class,
572 'style' => $style,
573 );
574 if ( ! empty( $nav_menu_name ) ) {
575 $extra_attributes['aria-label'] = $nav_menu_name;
576 }
577 $wrapper_attributes = get_block_wrapper_attributes( $extra_attributes );
578
579 if ( $is_responsive_menu ) {
580 $nav_element_directives = static::get_nav_element_directives( $is_interactive );
581 $wrapper_attributes .= ' ' . $nav_element_directives;
582 }
583
584 return $wrapper_attributes;
585 }
586
587 /**
588 * Gets the nav element directives.
589 *
590 * @since 6.5.0
591 *
592 * @param bool $is_interactive Whether the block is interactive.
593 * @return string the directives for the navigation element.
594 */
595 private static function get_nav_element_directives( $is_interactive ) {
596 if ( ! $is_interactive ) {
597 return '';
598 }
599 // When adding to this array be mindful of security concerns.
600 $nav_element_context = wp_interactivity_data_wp_context(
601 array(
602 'overlayOpenedBy' => array(
603 'click' => false,
604 'hover' => false,
605 'focus' => false,
606 ),
607 'type' => 'overlay',
608 'roleAttribute' => '',
609 'ariaLabel' => __( 'Menu' ),
610 )
611 );
612 $nav_element_directives = '
613 data-wp-interactive="core/navigation" '
614 . $nav_element_context;
615
616 return $nav_element_directives;
617 }
618
619 /**
620 * Handle view script module loading.
621 *
622 * @since 6.5.0
623 *
624 * @param array $attributes The block attributes.
625 * @param WP_Block $block The parsed block.
626 * @param WP_Block_List $inner_blocks The list of inner blocks.
627 */
628 private static function handle_view_script_module_loading( $attributes, $block, $inner_blocks ) {
629 if ( static::is_interactive( $attributes, $inner_blocks ) ) {
630 wp_enqueue_script_module( '@wordpress/block-library/navigation/view' );
631 }
632 }
633
634 /**
635 * Returns the markup for the navigation block.
636 *
637 * @since 6.5.0
638 *
639 * @param array $attributes The block attributes.
640 * @param WP_Block_List $inner_blocks The list of inner blocks.
641 * @return string Returns the navigation wrapper markup.
642 */
643 private static function get_wrapper_markup( $attributes, $inner_blocks ) {
644 $inner_blocks_html = static::get_inner_blocks_html( $attributes, $inner_blocks );
645 if ( static::is_responsive( $attributes ) ) {
646 return static::get_responsive_container_markup( $attributes, $inner_blocks, $inner_blocks_html );
647 }
648 return $inner_blocks_html;
649 }
650
651 /**
652 * Returns a unique name for the navigation.
653 *
654 * @since 6.5.0
655 *
656 * @param array $attributes The block attributes.
657 * @return string Returns a unique name for the navigation.
658 */
659 private static function get_unique_navigation_name( $attributes ) {
660 $nav_menu_name = static::get_navigation_name( $attributes );
661
662 // If the menu name has been used previously then append an ID
663 // to the name to ensure uniqueness across a given post.
664 if ( isset( static::$seen_menu_names[ $nav_menu_name ] ) && static::$seen_menu_names[ $nav_menu_name ] > 1 ) {
665 $count = static::$seen_menu_names[ $nav_menu_name ];
666 $nav_menu_name = $nav_menu_name . ' ' . ( $count );
667 }
668
669 return $nav_menu_name;
670 }
671
672 /**
673 * Renders the navigation block.
674 *
675 * @since 6.5.0
676 *
677 * @param array $attributes The block attributes.
678 * @param string $content The saved content.
679 * @param WP_Block $block The parsed block.
680 * @return string Returns the navigation block markup.
681 */
682 public static function render( $attributes, $content, $block ) {
683 /**
684 * Deprecated:
685 * The rgbTextColor and rgbBackgroundColor attributes
686 * have been deprecated in favor of
687 * customTextColor and customBackgroundColor ones.
688 * Move the values from old attrs to the new ones.
689 */
690 if ( isset( $attributes['rgbTextColor'] ) && empty( $attributes['textColor'] ) ) {
691 $attributes['customTextColor'] = $attributes['rgbTextColor'];
692 }
693
694 if ( isset( $attributes['rgbBackgroundColor'] ) && empty( $attributes['backgroundColor'] ) ) {
695 $attributes['customBackgroundColor'] = $attributes['rgbBackgroundColor'];
696 }
697
698 unset( $attributes['rgbTextColor'], $attributes['rgbBackgroundColor'] );
699
700 $inner_blocks = static::get_inner_blocks( $attributes, $block );
701 // Prevent navigation blocks referencing themselves from rendering.
702 if ( block_core_navigation_block_contains_core_navigation( $inner_blocks ) ) {
703 return '';
704 }
705
706 static::handle_view_script_module_loading( $attributes, $block, $inner_blocks );
707
708 return sprintf(
709 '<nav %1$s>%2$s</nav>',
710 static::get_nav_wrapper_attributes( $attributes, $inner_blocks ),
711 static::get_wrapper_markup( $attributes, $inner_blocks )
712 );
713 }
714}
715
716// These functions are used for the __unstableLocation feature and only active
717// when the gutenberg plugin is active.
718if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
719 /**
720 * Returns the menu items for a WordPress menu location.
721 *
722 * @since 5.9.0
723 *
724 * @param string $location The menu location.
725 * @return array Menu items for the location.
726 */
727 function block_core_navigation_get_menu_items_at_location( $location ) {
728 if ( empty( $location ) ) {
729 return;
730 }
731
732 // Build menu data. The following approximates the code in
733 // `wp_nav_menu()` and `gutenberg_output_block_nav_menu`.
734
735 // Find the location in the list of locations, returning early if the
736 // location can't be found.
737 $locations = get_nav_menu_locations();
738 if ( ! isset( $locations[ $location ] ) ) {
739 return;
740 }
741
742 // Get the menu from the location, returning early if there is no
743 // menu or there was an error.
744 $menu = wp_get_nav_menu_object( $locations[ $location ] );
745 if ( ! $menu || is_wp_error( $menu ) ) {
746 return;
747 }
748
749 $menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
750 _wp_menu_item_classes_by_context( $menu_items );
751
752 return $menu_items;
753 }
754
755
756 /**
757 * Sorts a standard array of menu items into a nested structure keyed by the
758 * id of the parent menu.
759 *
760 * @since 5.9.0
761 *
762 * @param array $menu_items Menu items to sort.
763 * @return array An array keyed by the id of the parent menu where each element
764 * is an array of menu items that belong to that parent.
765 */
766 function block_core_navigation_sort_menu_items_by_parent_id( $menu_items ) {
767 $sorted_menu_items = array();
768 foreach ( (array) $menu_items as $menu_item ) {
769 $sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
770 }
771 unset( $menu_items, $menu_item );
772
773 $menu_items_by_parent_id = array();
774 foreach ( $sorted_menu_items as $menu_item ) {
775 $menu_items_by_parent_id[ $menu_item->menu_item_parent ][] = $menu_item;
776 }
777
778 return $menu_items_by_parent_id;
779 }
780
781 /**
782 * Gets the inner blocks for the navigation block from the unstable location attribute.
783 *
784 * @since 6.5.0
785 *
786 * @param array $attributes The block attributes.
787 * @return WP_Block_List Returns the inner blocks for the navigation block.
788 */
789 function block_core_navigation_get_inner_blocks_from_unstable_location( $attributes ) {
790 $menu_items = block_core_navigation_get_menu_items_at_location( $attributes['__unstableLocation'] );
791 if ( empty( $menu_items ) ) {
792 return new WP_Block_List( array(), $attributes );
793 }
794
795 $menu_items_by_parent_id = block_core_navigation_sort_menu_items_by_parent_id( $menu_items );
796 $parsed_blocks = block_core_navigation_parse_blocks_from_menu_items( $menu_items_by_parent_id[0], $menu_items_by_parent_id );
797 return new WP_Block_List( $parsed_blocks, $attributes );
798 }
799}
800
801/**
802 * Add Interactivity API directives to the navigation-submenu and page-list
803 * blocks markup using the Tag Processor.
804 *
805 * @since 6.3.0
806 *
807 * @param WP_HTML_Tag_Processor $tags Markup of the navigation block.
808 * @param array $block_attributes Block attributes.
809 *
810 * @return string Submenu markup with the directives injected.
811 */
812function block_core_navigation_add_directives_to_submenu( $tags, $block_attributes ) {
813 while ( $tags->next_tag(
814 array(
815 'tag_name' => 'LI',
816 'class_name' => 'has-child',
817 )
818 ) ) {
819 // Add directives to the parent `<li>`.
820 $tags->set_attribute( 'data-wp-interactive', 'core/navigation' );
821 $tags->set_attribute( 'data-wp-context', '{ "submenuOpenedBy": { "click": false, "hover": false, "focus": false }, "type": "submenu", "modal": null, "previousFocus": null }' );
822 $tags->set_attribute( 'data-wp-watch', 'callbacks.initMenu' );
823 $tags->set_attribute( 'data-wp-on--focusout', 'actions.handleMenuFocusout' );
824 $tags->set_attribute( 'data-wp-on--keydown', 'actions.handleMenuKeydown' );
825
826 // This is a fix for Safari. Without it, Safari doesn't change the active
827 // element when the user clicks on a button. It can be removed once we add
828 // an overlay to capture the clicks, instead of relying on the focusout
829 // event.
830 $tags->set_attribute( 'tabindex', '-1' );
831
832 if ( ! isset( $block_attributes['openSubmenusOnClick'] ) || false === $block_attributes['openSubmenusOnClick'] ) {
833 $tags->set_attribute( 'data-wp-on--mouseenter', 'actions.openMenuOnHover' );
834 $tags->set_attribute( 'data-wp-on--mouseleave', 'actions.closeMenuOnHover' );
835 }
836
837 // Add directives to the toggle submenu button.
838 if ( $tags->next_tag(
839 array(
840 'tag_name' => 'BUTTON',
841 'class_name' => 'wp-block-navigation-submenu__toggle',
842 )
843 ) ) {
844 $tags->set_attribute( 'data-wp-on--click', 'actions.toggleMenuOnClick' );
845 $tags->set_attribute( 'data-wp-bind--aria-expanded', 'state.isMenuOpen' );
846 // The `aria-expanded` attribute for SSR is already added in the submenu block.
847 }
848 // Add directives to the submenu.
849 if ( $tags->next_tag(
850 array(
851 'tag_name' => 'UL',
852 'class_name' => 'wp-block-navigation__submenu-container',
853 )
854 ) ) {
855 $tags->set_attribute( 'data-wp-on--focus', 'actions.openMenuOnFocus' );
856 }
857
858 // Iterate through subitems if exist.
859 block_core_navigation_add_directives_to_submenu( $tags, $block_attributes );
860 }
861 return $tags->get_updated_html();
862}
863
864/**
865 * Build an array with CSS classes and inline styles defining the colors
866 * which will be applied to the navigation markup in the front-end.
867 *
868 * @since 5.9.0
869 *
870 * @param array $attributes Navigation block attributes.
871 *
872 * @return array Colors CSS classes and inline styles.
873 */
874function block_core_navigation_build_css_colors( $attributes ) {
875 $colors = array(
876 'css_classes' => array(),
877 'inline_styles' => '',
878 'overlay_css_classes' => array(),
879 'overlay_inline_styles' => '',
880 );
881
882 // Text color.
883 $has_named_text_color = array_key_exists( 'textColor', $attributes );
884 $has_custom_text_color = array_key_exists( 'customTextColor', $attributes );
885
886 // If has text color.
887 if ( $has_custom_text_color || $has_named_text_color ) {
888 // Add has-text-color class.
889 $colors['css_classes'][] = 'has-text-color';
890 }
891
892 if ( $has_named_text_color ) {
893 // Add the color class.
894 $colors['css_classes'][] = sprintf( 'has-%s-color', $attributes['textColor'] );
895 } elseif ( $has_custom_text_color ) {
896 // Add the custom color inline style.
897 $colors['inline_styles'] .= sprintf( 'color: %s;', $attributes['customTextColor'] );
898 }
899
900 // Background color.
901 $has_named_background_color = array_key_exists( 'backgroundColor', $attributes );
902 $has_custom_background_color = array_key_exists( 'customBackgroundColor', $attributes );
903
904 // If has background color.
905 if ( $has_custom_background_color || $has_named_background_color ) {
906 // Add has-background class.
907 $colors['css_classes'][] = 'has-background';
908 }
909
910 if ( $has_named_background_color ) {
911 // Add the background-color class.
912 $colors['css_classes'][] = sprintf( 'has-%s-background-color', $attributes['backgroundColor'] );
913 } elseif ( $has_custom_background_color ) {
914 // Add the custom background-color inline style.
915 $colors['inline_styles'] .= sprintf( 'background-color: %s;', $attributes['customBackgroundColor'] );
916 }
917
918 // Overlay text color.
919 $has_named_overlay_text_color = array_key_exists( 'overlayTextColor', $attributes );
920 $has_custom_overlay_text_color = array_key_exists( 'customOverlayTextColor', $attributes );
921
922 // If has overlay text color.
923 if ( $has_custom_overlay_text_color || $has_named_overlay_text_color ) {
924 // Add has-text-color class.
925 $colors['overlay_css_classes'][] = 'has-text-color';
926 }
927
928 if ( $has_named_overlay_text_color ) {
929 // Add the overlay color class.
930 $colors['overlay_css_classes'][] = sprintf( 'has-%s-color', $attributes['overlayTextColor'] );
931 } elseif ( $has_custom_overlay_text_color ) {
932 // Add the custom overlay color inline style.
933 $colors['overlay_inline_styles'] .= sprintf( 'color: %s;', $attributes['customOverlayTextColor'] );
934 }
935
936 // Overlay background color.
937 $has_named_overlay_background_color = array_key_exists( 'overlayBackgroundColor', $attributes );
938 $has_custom_overlay_background_color = array_key_exists( 'customOverlayBackgroundColor', $attributes );
939
940 // If has overlay background color.
941 if ( $has_custom_overlay_background_color || $has_named_overlay_background_color ) {
942 // Add has-background class.
943 $colors['overlay_css_classes'][] = 'has-background';
944 }
945
946 if ( $has_named_overlay_background_color ) {
947 // Add the overlay background-color class.
948 $colors['overlay_css_classes'][] = sprintf( 'has-%s-background-color', $attributes['overlayBackgroundColor'] );
949 } elseif ( $has_custom_overlay_background_color ) {
950 // Add the custom overlay background-color inline style.
951 $colors['overlay_inline_styles'] .= sprintf( 'background-color: %s;', $attributes['customOverlayBackgroundColor'] );
952 }
953
954 return $colors;
955}
956
957/**
958 * Build an array with CSS classes and inline styles defining the font sizes
959 * which will be applied to the navigation markup in the front-end.
960 *
961 * @since 5.9.0
962 *
963 * @param array $attributes Navigation block attributes.
964 *
965 * @return array Font size CSS classes and inline styles.
966 */
967function block_core_navigation_build_css_font_sizes( $attributes ) {
968 // CSS classes.
969 $font_sizes = array(
970 'css_classes' => array(),
971 'inline_styles' => '',
972 );
973
974 $has_named_font_size = array_key_exists( 'fontSize', $attributes );
975 $has_custom_font_size = array_key_exists( 'customFontSize', $attributes );
976
977 if ( $has_named_font_size ) {
978 // Add the font size class.
979 $font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $attributes['fontSize'] );
980 } elseif ( $has_custom_font_size ) {
981 // Add the custom font size inline style.
982 $font_sizes['inline_styles'] = sprintf( 'font-size: %spx;', $attributes['customFontSize'] );
983 }
984
985 return $font_sizes;
986}
987
988/**
989 * Returns the top-level submenu SVG chevron icon.
990 *
991 * @since 5.9.0
992 *
993 * @return string
994 */
995function block_core_navigation_render_submenu_icon() {
996 return '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" focusable="false"><path d="M1.50002 4L6.00002 8L10.5 4" stroke-width="1.5"></path></svg>';
997}
998
999/**
1000 * Filter out empty "null" blocks from the block list.
1001 * 'parse_blocks' includes a null block with '\n\n' as the content when
1002 * it encounters whitespace. This is not a bug but rather how the parser
1003 * is designed.
1004 *
1005 * @since 5.9.0
1006 *
1007 * @param array $parsed_blocks the parsed blocks to be normalized.
1008 * @return array the normalized parsed blocks.
1009 */
1010function block_core_navigation_filter_out_empty_blocks( $parsed_blocks ) {
1011 $filtered = array_filter(
1012 $parsed_blocks,
1013 static function ( $block ) {
1014 return isset( $block['blockName'] );
1015 }
1016 );
1017
1018 // Reset keys.
1019 return array_values( $filtered );
1020}
1021
1022/**
1023 * Returns true if the navigation block contains a nested navigation block.
1024 *
1025 * @since 6.2.0
1026 *
1027 * @param WP_Block_List $inner_blocks Inner block instance to be normalized.
1028 * @return bool true if the navigation block contains a nested navigation block.
1029 */
1030function block_core_navigation_block_contains_core_navigation( $inner_blocks ) {
1031 foreach ( $inner_blocks as $block ) {
1032 if ( 'core/navigation' === $block->name ) {
1033 return true;
1034 }
1035 if ( $block->inner_blocks && block_core_navigation_block_contains_core_navigation( $block->inner_blocks ) ) {
1036 return true;
1037 }
1038 }
1039
1040 return false;
1041}
1042
1043/**
1044 * Retrieves the appropriate fallback to be used on the front of the
1045 * site when there is no menu assigned to the Nav block.
1046 *
1047 * This aims to mirror how the fallback mechanic for wp_nav_menu works.
1048 * See https://developer.wordpress.org/reference/functions/wp_nav_menu/#more-information.
1049 *
1050 * @since 5.9.0
1051 *
1052 * @return array the array of blocks to be used as a fallback.
1053 */
1054function block_core_navigation_get_fallback_blocks() {
1055 $page_list_fallback = array(
1056 array(
1057 'blockName' => 'core/page-list',
1058 'innerContent' => array(),
1059 'attrs' => array(),
1060 ),
1061 );
1062
1063 $registry = WP_Block_Type_Registry::get_instance();
1064
1065 // If `core/page-list` is not registered then return empty blocks.
1066 $fallback_blocks = $registry->is_registered( 'core/page-list' ) ? $page_list_fallback : array();
1067 $navigation_post = WP_Navigation_Fallback::get_fallback();
1068
1069 // Use the first non-empty Navigation as fallback if available.
1070 if ( $navigation_post ) {
1071 $parsed_blocks = parse_blocks( $navigation_post->post_content );
1072 $maybe_fallback = block_core_navigation_filter_out_empty_blocks( $parsed_blocks );
1073
1074 // Normalizing blocks may result in an empty array of blocks if they were all `null` blocks.
1075 // In this case default to the (Page List) fallback.
1076 $fallback_blocks = ! empty( $maybe_fallback ) ? $maybe_fallback : $fallback_blocks;
1077
1078 // Run Block Hooks algorithm to inject hooked blocks.
1079 // We have to run it here because we need the post ID of the Navigation block to track ignored hooked blocks.
1080 // TODO: See if we can move the apply_block_hooks_to_content_from_post_object() call
1081 // before the parse_blocks() call further above, to avoid the extra serialization/parsing.
1082 $markup = serialize_blocks( $fallback_blocks );
1083 $markup = apply_block_hooks_to_content_from_post_object( $markup, $navigation_post );
1084 $fallback_blocks = parse_blocks( $markup );
1085 }
1086
1087 /**
1088 * Filters the fallback experience for the Navigation block.
1089 *
1090 * Returning a falsey value will opt out of the fallback and cause the block not to render.
1091 * To customise the blocks provided return an array of blocks - these should be valid
1092 * children of the `core/navigation` block.
1093 *
1094 * @since 5.9.0
1095 *
1096 * @param array[] $fallback_blocks default fallback blocks provided by the default block mechanic.
1097 */
1098 return apply_filters( 'block_core_navigation_render_fallback', $fallback_blocks );
1099}
1100
1101/**
1102 * Iterate through all inner blocks recursively and get navigation link block's post IDs.
1103 *
1104 * @since 6.0.0
1105 *
1106 * @param WP_Block_List $inner_blocks Block list class instance.
1107 *
1108 * @return array Array of post IDs.
1109 */
1110function block_core_navigation_get_post_ids( $inner_blocks ) {
1111 $post_ids = array_map( 'block_core_navigation_from_block_get_post_ids', iterator_to_array( $inner_blocks ) );
1112 return array_unique( array_merge( ...$post_ids ) );
1113}
1114
1115/**
1116 * Get post IDs from a navigation link block instance.
1117 *
1118 * @since 6.0.0
1119 *
1120 * @param WP_Block $block Instance of a block.
1121 *
1122 * @return array Array of post IDs.
1123 */
1124function block_core_navigation_from_block_get_post_ids( $block ) {
1125 $post_ids = array();
1126
1127 if ( $block->inner_blocks ) {
1128 $post_ids = block_core_navigation_get_post_ids( $block->inner_blocks );
1129 }
1130
1131 if ( 'core/navigation-link' === $block->name || 'core/navigation-submenu' === $block->name ) {
1132 if ( $block->attributes && isset( $block->attributes['kind'] ) && 'post-type' === $block->attributes['kind'] && isset( $block->attributes['id'] ) ) {
1133 $post_ids[] = $block->attributes['id'];
1134 }
1135 }
1136
1137 return $post_ids;
1138}
1139
1140/**
1141 * Renders the `core/navigation` block on server.
1142 *
1143 * @since 5.9.0
1144 *
1145 * @param array $attributes The block attributes.
1146 * @param string $content The saved content.
1147 * @param WP_Block $block The parsed block.
1148 *
1149 * @return string Returns the navigation block markup.
1150 */
1151function render_block_core_navigation( $attributes, $content, $block ) {
1152 return WP_Navigation_Block_Renderer::render( $attributes, $content, $block );
1153}
1154
1155/**
1156 * Register the navigation block.
1157 *
1158 * @since 5.9.0
1159 *
1160 * @uses render_block_core_navigation()
1161 * @throws WP_Error An WP_Error exception parsing the block definition.
1162 */
1163function register_block_core_navigation() {
1164 register_block_type_from_metadata(
1165 __DIR__ . '/navigation',
1166 array(
1167 'render_callback' => 'render_block_core_navigation',
1168 )
1169 );
1170}
1171
1172add_action( 'init', 'register_block_core_navigation' );
1173
1174/**
1175 * Filter that changes the parsed attribute values of navigation blocks contain typographic presets to contain the values directly.
1176 *
1177 * @since 5.9.0
1178 *
1179 * @param array $parsed_block The block being rendered.
1180 *
1181 * @return array The block being rendered without typographic presets.
1182 */
1183function block_core_navigation_typographic_presets_backcompatibility( $parsed_block ) {
1184 if ( 'core/navigation' === $parsed_block['blockName'] ) {
1185 $attribute_to_prefix_map = array(
1186 'fontStyle' => 'var:preset|font-style|',
1187 'fontWeight' => 'var:preset|font-weight|',
1188 'textDecoration' => 'var:preset|text-decoration|',
1189 'textTransform' => 'var:preset|text-transform|',
1190 );
1191 foreach ( $attribute_to_prefix_map as $style_attribute => $prefix ) {
1192 if ( ! empty( $parsed_block['attrs']['style']['typography'][ $style_attribute ] ) ) {
1193 $prefix_len = strlen( $prefix );
1194 $attribute_value = &$parsed_block['attrs']['style']['typography'][ $style_attribute ];
1195 if ( 0 === strncmp( $attribute_value, $prefix, $prefix_len ) ) {
1196 $attribute_value = substr( $attribute_value, $prefix_len );
1197 }
1198 if ( 'textDecoration' === $style_attribute && 'strikethrough' === $attribute_value ) {
1199 $attribute_value = 'line-through';
1200 }
1201 }
1202 }
1203 }
1204
1205 return $parsed_block;
1206}
1207
1208add_filter( 'render_block_data', 'block_core_navigation_typographic_presets_backcompatibility' );
1209
1210/**
1211 * Turns menu item data into a nested array of parsed blocks
1212 *
1213 * @since 5.9.0
1214 *
1215 * @deprecated 6.3.0 Use WP_Navigation_Fallback::parse_blocks_from_menu_items() instead.
1216 *
1217 * @param array $menu_items An array of menu items that represent
1218 * an individual level of a menu.
1219 * @param array $menu_items_by_parent_id An array keyed by the id of the
1220 * parent menu where each element is an
1221 * array of menu items that belong to
1222 * that parent.
1223 * @return array An array of parsed block data.
1224 */
1225function block_core_navigation_parse_blocks_from_menu_items( $menu_items, $menu_items_by_parent_id ) {
1226
1227 _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::parse_blocks_from_menu_items' );
1228
1229 if ( empty( $menu_items ) ) {
1230 return array();
1231 }
1232
1233 $blocks = array();
1234
1235 foreach ( $menu_items as $menu_item ) {
1236 $class_name = ! empty( $menu_item->classes ) ? implode( ' ', (array) $menu_item->classes ) : null;
1237 $id = ( null !== $menu_item->object_id && 'custom' !== $menu_item->object ) ? $menu_item->object_id : null;
1238 $opens_in_new_tab = null !== $menu_item->target && '_blank' === $menu_item->target;
1239 $rel = ( null !== $menu_item->xfn && '' !== $menu_item->xfn ) ? $menu_item->xfn : null;
1240 $kind = null !== $menu_item->type ? str_replace( '_', '-', $menu_item->type ) : 'custom';
1241
1242 $block = array(
1243 'blockName' => isset( $menu_items_by_parent_id[ $menu_item->ID ] ) ? 'core/navigation-submenu' : 'core/navigation-link',
1244 'attrs' => array(
1245 'className' => $class_name,
1246 'description' => $menu_item->description,
1247 'id' => $id,
1248 'kind' => $kind,
1249 'label' => $menu_item->title,
1250 'opensInNewTab' => $opens_in_new_tab,
1251 'rel' => $rel,
1252 'title' => $menu_item->attr_title,
1253 'type' => $menu_item->object,
1254 'url' => $menu_item->url,
1255 ),
1256 );
1257
1258 $block['innerBlocks'] = isset( $menu_items_by_parent_id[ $menu_item->ID ] )
1259 ? block_core_navigation_parse_blocks_from_menu_items( $menu_items_by_parent_id[ $menu_item->ID ], $menu_items_by_parent_id )
1260 : array();
1261 $block['innerContent'] = array_map( 'serialize_block', $block['innerBlocks'] );
1262
1263 $blocks[] = $block;
1264 }
1265
1266 return $blocks;
1267}
1268
1269/**
1270 * Get the classic navigation menu to use as a fallback.
1271 *
1272 * @since 6.2.0
1273 *
1274 * @deprecated 6.3.0 Use WP_Navigation_Fallback::get_classic_menu_fallback() instead.
1275 *
1276 * @return object WP_Term The classic navigation.
1277 */
1278function block_core_navigation_get_classic_menu_fallback() {
1279
1280 _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::get_classic_menu_fallback' );
1281
1282 $classic_nav_menus = wp_get_nav_menus();
1283
1284 // If menus exist.
1285 if ( $classic_nav_menus && ! is_wp_error( $classic_nav_menus ) ) {
1286 // Handles simple use case where user has a classic menu and switches to a block theme.
1287
1288 // Returns the menu assigned to location `primary`.
1289 $locations = get_nav_menu_locations();
1290 if ( isset( $locations['primary'] ) ) {
1291 $primary_menu = wp_get_nav_menu_object( $locations['primary'] );
1292 if ( $primary_menu ) {
1293 return $primary_menu;
1294 }
1295 }
1296
1297 // Returns a menu if `primary` is its slug.
1298 foreach ( $classic_nav_menus as $classic_nav_menu ) {
1299 if ( 'primary' === $classic_nav_menu->slug ) {
1300 return $classic_nav_menu;
1301 }
1302 }
1303
1304 // Otherwise return the most recently created classic menu.
1305 usort(
1306 $classic_nav_menus,
1307 static function ( $a, $b ) {
1308 return $b->term_id - $a->term_id;
1309 }
1310 );
1311 return $classic_nav_menus[0];
1312 }
1313}
1314
1315/**
1316 * Converts a classic navigation to blocks.
1317 *
1318 * @since 6.2.0
1319 *
1320 * @deprecated 6.3.0 Use WP_Navigation_Fallback::get_classic_menu_fallback_blocks() instead.
1321 *
1322 * @param object $classic_nav_menu WP_Term The classic navigation object to convert.
1323 * @return array the normalized parsed blocks.
1324 */
1325function block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_menu ) {
1326
1327 _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::get_classic_menu_fallback_blocks' );
1328
1329 // BEGIN: Code that already exists in wp_nav_menu().
1330 $menu_items = wp_get_nav_menu_items( $classic_nav_menu->term_id, array( 'update_post_term_cache' => false ) );
1331
1332 // Set up the $menu_item variables.
1333 _wp_menu_item_classes_by_context( $menu_items );
1334
1335 $sorted_menu_items = array();
1336 foreach ( (array) $menu_items as $menu_item ) {
1337 $sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
1338 }
1339
1340 unset( $menu_items, $menu_item );
1341
1342 // END: Code that already exists in wp_nav_menu().
1343
1344 $menu_items_by_parent_id = array();
1345 foreach ( $sorted_menu_items as $menu_item ) {
1346 $menu_items_by_parent_id[ $menu_item->menu_item_parent ][] = $menu_item;
1347 }
1348
1349 $inner_blocks = block_core_navigation_parse_blocks_from_menu_items(
1350 isset( $menu_items_by_parent_id[0] )
1351 ? $menu_items_by_parent_id[0]
1352 : array(),
1353 $menu_items_by_parent_id
1354 );
1355
1356 return serialize_blocks( $inner_blocks );
1357}
1358
1359/**
1360 * If there's a classic menu then use it as a fallback.
1361 *
1362 * @since 6.2.0
1363 *
1364 * @deprecated 6.3.0 Use WP_Navigation_Fallback::create_classic_menu_fallback() instead.
1365 *
1366 * @return array the normalized parsed blocks.
1367 */
1368function block_core_navigation_maybe_use_classic_menu_fallback() {
1369
1370 _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::create_classic_menu_fallback' );
1371
1372 // See if we have a classic menu.
1373 $classic_nav_menu = block_core_navigation_get_classic_menu_fallback();
1374
1375 if ( ! $classic_nav_menu ) {
1376 return;
1377 }
1378
1379 // If we have a classic menu then convert it to blocks.
1380 $classic_nav_menu_blocks = block_core_navigation_get_classic_menu_fallback_blocks( $classic_nav_menu );
1381
1382 if ( empty( $classic_nav_menu_blocks ) ) {
1383 return;
1384 }
1385
1386 // Create a new navigation menu from the classic menu.
1387 $wp_insert_post_result = wp_insert_post(
1388 array(
1389 'post_content' => $classic_nav_menu_blocks,
1390 'post_title' => $classic_nav_menu->name,
1391 'post_name' => $classic_nav_menu->slug,
1392 'post_status' => 'publish',
1393 'post_type' => 'wp_navigation',
1394 ),
1395 true // So that we can check whether the result is an error.
1396 );
1397
1398 if ( is_wp_error( $wp_insert_post_result ) ) {
1399 return;
1400 }
1401
1402 // Fetch the most recently published navigation which will be the classic one created above.
1403 return block_core_navigation_get_most_recently_published_navigation();
1404}
1405
1406/**
1407 * Finds the most recently published `wp_navigation` Post.
1408 *
1409 * @since 6.1.0
1410 *
1411 * @deprecated 6.3.0 Use WP_Navigation_Fallback::get_most_recently_published_navigation() instead.
1412 *
1413 * @return WP_Post|null the first non-empty Navigation or null.
1414 */
1415function block_core_navigation_get_most_recently_published_navigation() {
1416
1417 _deprecated_function( __FUNCTION__, '6.3.0', 'WP_Navigation_Fallback::get_most_recently_published_navigation' );
1418
1419 // Default to the most recently created menu.
1420 $parsed_args = array(
1421 'post_type' => 'wp_navigation',
1422 'no_found_rows' => true,
1423 'update_post_meta_cache' => false,
1424 'update_post_term_cache' => false,
1425 'order' => 'DESC',
1426 'orderby' => 'date',
1427 'post_status' => 'publish',
1428 'posts_per_page' => 1, // get only the most recent.
1429 );
1430
1431 $navigation_post = new WP_Query( $parsed_args );
1432 if ( count( $navigation_post->posts ) > 0 ) {
1433 return $navigation_post->posts[0];
1434 }
1435
1436 return null;
1437}
1438
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1