PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.13.1
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.13.1
2.13.0 2.13.1 2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 All 80 releases
ablocks / includes / blocks / breadcrumb / block.php

block.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.13.1, at includes/blocks/breadcrumb/block.php

748 lines 25.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace ABlocks\Blocks\Breadcrumb;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 use ABlocks\Classes\BlockBaseAbstract;
9 use ABlocks\Classes\CssGeneratorV2;
10 use ABlocks\Helper;
11 use ABlocks\Controls\Color;
12 use ABlocks\Controls\Typography;
13 use ABlocks\Controls\Alignment;
14 use ABlocks\Controls\Dimensions;
15
16 class Block extends BlockBaseAbstract {
17 protected $block_name = 'breadcrumb';
18
19 public function build_css( $attributes ) {
20 $css_generator = new CssGeneratorV2( $attributes, $this->block_name );
21
22 $css_generator->add_class_styles(
23 '{{WRAPPER}} .ablocks-breadcrumbs',
24 $this->get_wrapper_css( $attributes ),
25 $this->get_wrapper_css( $attributes, 'Tablet' ),
26 $this->get_wrapper_css( $attributes, 'Mobile' )
27 );
28
29 // Normal link color
30 $css_generator->add_class_styles(
31 '{{WRAPPER}} div.ablocks-breadcrumbs span a',
32 $this->get_breadcrumb_normal_title_css( $attributes ),
33 $this->get_breadcrumb_normal_title_css( $attributes, 'Tablet' ),
34 $this->get_breadcrumb_normal_title_css( $attributes, 'Mobile' )
35 );
36
37 // Hover link color
38 $css_generator->add_class_styles(
39 '{{WRAPPER}} div.ablocks-breadcrumbs span a:hover',
40 $this->get_breadcrumb_hover_title_css( $attributes ),
41 $this->get_breadcrumb_hover_title_css( $attributes, 'Tablet' ),
42 $this->get_breadcrumb_hover_title_css( $attributes, 'Mobile' )
43 );
44
45 // Title (typography + color + direction)
46 $css_generator->add_class_styles(
47 '{{WRAPPER}} .ablocks-breadcrumbs__item',
48 $this->get_breadcrumb_title_css( $attributes ),
49 $this->get_breadcrumb_title_css( $attributes, 'Tablet' ),
50 $this->get_breadcrumb_title_css( $attributes, 'Mobile' )
51 );
52
53 // Separator (color + font-size)
54 $css_generator->add_class_styles(
55 '{{WRAPPER}} .ablocks-breadcrumbs__separator',
56 $this->get_breadcrumb_separator_css( $attributes ),
57 $this->get_breadcrumb_separator_css( $attributes, 'Tablet' ),
58 $this->get_breadcrumb_separator_css( $attributes, 'Mobile' )
59 );
60 $css_generator->add_class_styles(
61 '{{WRAPPER}} .ablocks-breadcrumbs___before-image , {{WRAPPER}} .ablocks-breadcrumbs__before-text',
62 $this->get_breadcrumb_before_text_image_css( $attributes ),
63 $this->get_breadcrumb_before_text_image_css( $attributes, 'Tablet' ),
64 $this->get_breadcrumb_before_text_image_css( $attributes, 'Mobile' )
65 );
66
67 return $css_generator->generate_css();
68
69 }
70
71
72 public function get_wrapper_css( $attributes, $device = '' ) {
73 $css = [];
74
75 if ( ! empty( $attributes['breadcrumbSpaceBetween'] ) ) {
76 $css['gap'] = $attributes['breadcrumbSpaceBetween'] . 'px';
77 }
78
79 if ( isset( $attributes['positionBreadcrumb'][ 'value' . $device ] ) ) {
80 $css['justify-content'] = $attributes['positionBreadcrumb'][ 'value' . $device ];
81 }
82 return array_merge(
83 $css,
84 );
85
86 }
87
88 public function get_breadcrumb_title_css( $attributes, $device = '' ) {
89 $css = [];
90
91 if ( ! empty( $attributes['breadcrumbTitlecolor'] ) ) {
92 $css['color'] = Color::get_css( $attributes['breadcrumbTitlecolor'] );
93 }
94
95 $dir = isset( $attributes['taxonomyTitleDirection'] ) ? $attributes['taxonomyTitleDirection'] : null;
96 $dirCss = ! is_null( $dir ) ? get_alignment_css( $dir, 'justify-content', $device ) : [];
97 if ( ! empty( $attributes['breadcrumbItemBackground'] ) ) {
98 $css['background-color'] = Color::get_css( $attributes['breadcrumbItemBackground'] );
99 }
100 $typographyValueGlobal = ! empty( $attributes['breadcrumbTitleTypographyGlobal'] ) ? $attributes['breadcrumbTitleTypographyGlobal'] : '';
101 return array_merge(
102 $css, (array) $dirCss,
103 Typography::get_css( $attributes['breadcrumbTitleTypography'] ?? [], '', $device, $typographyValueGlobal ),
104 Dimensions::get_css( $attributes['breadcrumbItemPadding'] ?? [], 'padding', $device ),
105 Dimensions::get_css( $attributes['BreadcrumbBorderRadius'] ?? [], 'border-radius', $device ),
106 );
107 }
108
109 public function get_breadcrumb_normal_title_css( $attributes, $device = '' ) {
110 $css = [];
111
112 if ( ! empty( $attributes['breadcrumbLinkcolor'] ) ) {
113 $css['color'] = Color::get_css( $attributes['breadcrumbLinkcolor'] );
114 }
115
116 return $css;
117 }
118
119 public function get_breadcrumb_hover_title_css( $attributes, $device = '' ) {
120 $css = [];
121
122 if ( ! empty( $attributes['breadcrumbHoverLinkcolor'] ) ) {
123 $css['color'] = Color::get_css( $attributes['breadcrumbHoverLinkcolor'] );
124 }
125
126 return $css;
127 }
128
129 public function get_breadcrumb_separator_css( $attributes, $device = '' ) {
130 $css = [];
131
132 if ( ! empty( $attributes['breadcrumbseparatorcolor'] ) ) {
133 $css['color'] = Color::get_css( $attributes['breadcrumbseparatorcolor'] );
134 }
135
136 if ( ! empty( $attributes['breadcrumbseparsize'] ) ) {
137 // IMPORTANT: CSS property is kebab-case
138 $css['font-size'] = $attributes['breadcrumbseparsize'] . 'px';
139 }
140
141 return $css;
142 }
143 public function get_breadcrumb_before_text_image_css( $attributes, $device = '' ) {
144 $css = [];
145
146 if ( ! empty( $attributes['beforeBreadcrumbBackgroundcolor'] ) ) {
147 $css['background-color'] = Color::get_css( $attributes['beforeBreadcrumbBackgroundcolor'] );
148 }
149
150 return array_merge(
151 $css,
152 Dimensions::get_css( $attributes['beforeBreadcrumbPaddingcolor'] ?? [], 'padding', $device ),
153 Dimensions::get_css( $attributes['beforeBreadcrumbBorderRadius'] ?? [], 'border-radius', $device ),
154 );
155 }
156
157
158 public function render( $attributes = [] ) {
159 $breadcrumb_normalize = $this->normalize( $attributes );
160 $breadcrumb_items = $this->build_items( $breadcrumb_normalize );
161 if ( empty( $breadcrumb_items ) ) {
162 return;
163 }
164 $breadcrumb_items = $this->limit_items( $breadcrumb_items, (int) $breadcrumb_normalize['max'] );
165 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
166 echo $this->render_html_breadcrumbs( $breadcrumb_items, $breadcrumb_normalize );
167 }
168
169 /**
170 * Defaults
171 */
172 private function normalize( $attributes ) {
173 $separator = isset( $attributes['SeparatorChange'] ) && $attributes['SeparatorChange'] !== ''
174 ? (string) $attributes['SeparatorChange']
175 : '>';
176
177 $home_text = ( isset( $attributes['homeBreadcrumbs'] ) && $attributes['homeBreadcrumbs'] !== '' )
178 ? (string) $attributes['homeBreadcrumbs']
179 : __( 'Home', 'ablocks' );
180
181 $beforeBreadcrumbImage = ( isset( $attributes['beforeBreadcrumbImage'] ) && $attributes['beforeBreadcrumbImage'] !== '' )
182 ? (string) $attributes['beforeBreadcrumbImage']
183 : ''; // __( '', 'ablocks' );
184
185 $beforeBreadcrumbText = ( isset( $attributes['beforeBreadcrumbText'] ) && $attributes['beforeBreadcrumbText'] !== '' )
186 ? (string) $attributes['beforeBreadcrumbText']
187 : __( 'Before Text', 'ablocks' );
188
189 $beforeBreadcrumbTextImage = ( isset( $attributes['beforeBreadcrumbTextImage'] ) && $attributes['beforeBreadcrumbTextImage'] !== '' )
190 ? (string) $attributes['beforeBreadcrumbTextImage']
191 : ''; // __( '', 'ablocks' );
192
193 $beforeTextImage = ( isset( $attributes['beforeTextImage'] ) && $attributes['beforeTextImage'] !== '' )
194 ? (string) $attributes['beforeTextImage']
195 : ''; // __( '', 'ablocks' );
196
197 $beforeSeparator = ( isset( $attributes['beforeSeparator'] ) && $attributes['beforeSeparator'] !== '' )
198 ? (string) $attributes['beforeSeparator']
199 : ''; // __( '', 'ablocks' );
200
201 $enable_faq_schema = false;
202 if ( isset( $attributes['enableFaqSchema'] ) ) {
203 $parsed_enable_faq_schema = filter_var( $attributes['enableFaqSchema'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
204 $enable_faq_schema = ( null === $parsed_enable_faq_schema ) ? false : $parsed_enable_faq_schema;
205 }
206
207 return wp_parse_args( (array) $attributes, [
208 'separator' => $separator,
209 'home_text' => $home_text,
210 'before_breadcrumb_image' => $beforeBreadcrumbImage,
211 'before_breadcrumb_text' => $beforeBreadcrumbText,
212 'before_breadcrumb_text_image' => $beforeBreadcrumbTextImage,
213 'before_text_image' => $beforeTextImage,
214 'before_separator' => $beforeSeparator,
215 'enableFaqSchema' => $enable_faq_schema,
216 'include_current' => true,
217 'tax' => '',
218 'max' => 0,
219 ] );
220 }
221
222 /**
223 * Detect editor (post/site editor, AJAX, REST block render)
224 */
225 private function in_editor() : bool {
226 // More precise REST check: block renderer routes
227 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
228 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
229 $uri = $_SERVER['REQUEST_URI'] ?? '';
230 if ( strpos( $uri, '/wp/v2/block-renderer' ) !== false
231 || strpos( $uri, '/wp-block-editor/v1/block-renderer' ) !== false ) {
232 return true;
233 }
234 }
235
236 if ( is_admin() ) {
237 if ( function_exists( 'get_current_screen' ) ) {
238 $scr = get_current_screen();
239 if ( $scr ) {
240 if ( ! empty( $scr->is_block_editor ) ) {
241 return true;
242 }
243 if ( isset( $scr->base ) && $scr->base === 'site-editor' ) {
244 return true;
245 }
246 }
247 }
248 if ( function_exists( 'wp_doing_ajax' ) && wp_doing_ajax() ) {
249 return true;
250 }
251 }
252 return false;
253 }
254
255 /**
256 * Build breadcrumb items (editor-aware)
257 * Each: ['label' => string, 'url' => string|false]
258 */
259 private function build_items( array $breadcrumb_normalize ) {
260 if ( $this->in_editor() ) {
261 return $this->build_items_editor( $breadcrumb_normalize );
262 }
263 $breadcrumb_items = [];
264
265 // Front page
266 if ( is_front_page() ) {
267 $this->add_item( $breadcrumb_items, $breadcrumb_normalize['home_text'] );
268 return $breadcrumb_items;
269 }
270
271 // Home
272 $this->add_item( $breadcrumb_items, $breadcrumb_normalize['home_text'], home_url( '/' ) );
273
274 // Posts page (static front)
275 if ( is_home() && ! is_front_page() ) {
276 $pid = (int) get_option( 'page_for_posts' );
277 $this->add_item( $breadcrumb_items, $pid ? get_the_title( $pid ) : __( 'Blog', 'ablocks' ) );
278 return $breadcrumb_items;
279 }
280
281 // Singular
282 if ( is_singular() ) {
283 global $post;
284
285 if ( ! $post ) {
286 if ( ! empty( $breadcrumb_normalize['include_current'] ) ) {
287 $this->add_item( $breadcrumb_items, $this->current_title_safe() );
288 }
289 return $breadcrumb_items;
290 }
291
292 $breadcrumb_pt = get_post_type( $post );
293 $breadcrumb_pto = $breadcrumb_pt ? get_post_type_object( $breadcrumb_pt ) : null;
294
295 // WooCommerce: prepend Shop for product
296 $is_wc_product = ( 'product' === $breadcrumb_pt ) && function_exists( 'wc_get_page_id' ) && is_singular( 'product' );
297 if ( $is_wc_product ) {
298 $shop_id = wc_get_page_id( 'shop' );
299 if ( $shop_id && 'publish' === get_post_status( $shop_id ) ) {
300 $this->add_item( $breadcrumb_items, get_the_title( $shop_id ), get_permalink( $shop_id ) );
301 }
302 }
303
304 // Post type label/archive (avoid for product since Shop already added)
305 if ( ! $is_wc_product ) {
306 $this->add_post_type_item( $breadcrumb_items, $breadcrumb_pt, $breadcrumb_pto, [ 'post', 'page' ] );
307 }
308
309 // Hierarchical (page / hierarchical CPT)
310 if ( is_page() || ( $breadcrumb_pto && is_post_type_hierarchical( $breadcrumb_pt ) ) ) {
311 $this->add_post_ancestors( $breadcrumb_items, $post );
312
313 // Also include taxonomy trail (categories/tags) on single pages if available.
314 $this->append_taxonomy_trail( $breadcrumb_items, $post->ID, $breadcrumb_pt, $breadcrumb_normalize['tax'] );
315 } else {
316 // Non-hierarchical: taxonomy trail (if any).
317 $this->append_taxonomy_trail( $breadcrumb_items, $post->ID, $breadcrumb_pt, $breadcrumb_normalize['tax'] );
318 }//end if
319
320 if ( ! empty( $breadcrumb_normalize['include_current'] ) ) {
321 $this->add_item( $breadcrumb_items, $this->current_title_safe( $post ) );
322 }
323 return $breadcrumb_items;
324 }//end if
325
326 // Tax archives
327 if ( is_category() || is_tag() || is_tax() ) {
328 $term = get_queried_object();
329 return ( $term && ! is_wp_error( $term ) )
330 ? $this->build_taxonomy_archive_items( $breadcrumb_items, $term )
331 : $breadcrumb_items;
332 }//end if
333
334 // Post type archive
335 if ( is_post_type_archive() ) {
336 $breadcrumb_pt = get_query_var( 'post_type' );
337 if ( is_array( $breadcrumb_pt ) ) {
338 $breadcrumb_pt = reset( $breadcrumb_pt ); }
339 $po = $breadcrumb_pt ? get_post_type_object( $breadcrumb_pt ) : null;
340 if ( $po ) {
341 $this->add_item( $breadcrumb_items, $po->labels->name );
342 return $breadcrumb_items;
343 }
344 }
345
346 // Author
347 if ( is_author() ) {
348 $breadcrumb_u = get_queried_object();
349 if ( $breadcrumb_u ) {
350 $this->add_item( $breadcrumb_items, sprintf( __( 'Author: %s', 'ablocks' ), $breadcrumb_u->display_name ) );
351 return $breadcrumb_items;
352 }
353 }
354
355 // Date
356 if ( is_year() || is_month() || is_day() ) {
357 $this->add_item( $breadcrumb_items, get_the_date( is_year() ? 'Y' : ( is_month() ? 'F Y' : 'F j, Y' ) ) );
358 return $breadcrumb_items;
359 }
360
361 // Search
362 if ( is_search() ) {
363 $this->add_item( $breadcrumb_items, sprintf( __( 'Search: %s', 'ablocks' ), get_search_query() ) );
364 return $breadcrumb_items;
365 }
366
367 // 404
368 if ( is_404() ) {
369 $this->add_item( $breadcrumb_items, __( '404 Not Found', 'ablocks' ) );
370 return $breadcrumb_items;
371 }
372
373 return $breadcrumb_items;
374 }
375
376 /**
377 * Editor builder (mirrors frontend; robust current title)
378 */
379 private function build_items_editor( array $breadcrumb_normalize ) : array {
380 $breadcrumb_items = [];
381 $home_label = (string) $breadcrumb_normalize['home_text']; // attributes → fallback "Home"
382 $this->add_item( $breadcrumb_items, $home_label, home_url( '/' ) );
383
384 global $post;
385 if ( $post instanceof \WP_Post ) {
386 $breadcrumb_pt = get_post_type( $post );
387 $breadcrumb_pto = $breadcrumb_pt ? get_post_type_object( $breadcrumb_pt ) : null;
388
389 if ( is_post_type_hierarchical( $breadcrumb_pt ) ) {
390 $this->add_post_ancestors( $breadcrumb_items, $post );
391
392 // Also include taxonomy trail (categories/tags) on single pages if available.
393 $this->append_taxonomy_trail( $breadcrumb_items, $post->ID, $breadcrumb_pt, $breadcrumb_normalize['tax'] );
394 } else {
395 $this->add_post_type_item( $breadcrumb_items, $breadcrumb_pt, $breadcrumb_pto, [ 'post', 'page' ] );
396 $this->append_taxonomy_trail( $breadcrumb_items, $post->ID, $breadcrumb_pt, $breadcrumb_normalize['tax'] );
397 }//end if
398
399 if ( ! empty( $breadcrumb_normalize['include_current'] ) ) {
400 $this->add_item( $breadcrumb_items, $this->current_title_safe( $post ) );
401 }
402 return $breadcrumb_items;
403 }//end if
404
405 // No $post in editor (e.g., site editor template) → sample trail
406 $this->add_item( $breadcrumb_items, __( 'Template', 'ablocks' ), home_url( '/Template/' ) );
407 if ( ! empty( $breadcrumb_normalize['include_current'] ) ) {
408 $this->add_item( $breadcrumb_items, __( 'Currents Page', 'ablocks' ) );
409 }
410 return $breadcrumb_items;
411 }
412
413 /**
414 * Safe current title (fallbacks when get_the_title() empty in editor)
415 */
416 private function current_title_safe( $p = null ) : string {
417 if ( $p instanceof \WP_Post ) {
418 $breadcrumb_t = get_the_title( $p );
419 if ( $breadcrumb_t === '' ) {
420 $breadcrumb_t = $p->post_title;
421 }
422 if ( $breadcrumb_t === '' ) {
423 $breadcrumb_t = __( '(no title)', 'ablocks' );
424 }
425 return $breadcrumb_t;
426 }
427 $breadcrumb_t = single_post_title( '', false );
428 if ( $breadcrumb_t === '' ) {
429 $breadcrumb_t = __( '(no title)', 'ablocks' );
430 }
431 return $breadcrumb_t;
432 }
433
434 /**
435 * Add a breadcrumb item.
436 */
437 private function add_item( array &$breadcrumb_items, $label, $url = false ) : void {
438 $breadcrumb_items[] = [
439 'label' => (string) $label,
440 'url' => $url
441 ];
442 }
443
444 /**
445 * Add a term breadcrumb item (linked).
446 */
447 private function add_term_item( array &$breadcrumb_items, $term ) : void {
448 if ( ! $term || is_wp_error( $term ) ) {
449 return;
450 }
451 $link = get_term_link( $term );
452 $this->add_item( $breadcrumb_items, $term->name, is_wp_error( $link ) ? false : $link );
453 }
454
455 /**
456 * Add post ancestors (linked).
457 */
458 private function add_post_ancestors( array &$breadcrumb_items, $post ) : void {
459 foreach ( array_reverse( get_post_ancestors( $post ) ) as $aid ) {
460 if ( (int) $aid === (int) get_option( 'page_on_front' ) ) {
461 continue;
462 }
463 $this->add_item( $breadcrumb_items, get_the_title( $aid ), get_permalink( $aid ) );
464 }
465 }
466
467 /**
468 * Add a post type item.
469 */
470 private function add_post_type_item( array &$breadcrumb_items, $post_type, $post_type_obj = null, array $skip_types = [] ) : bool {
471 if ( $post_type && in_array( $post_type, $skip_types, true ) ) {
472 return false;
473 }
474 $breadcrumb_pto = $post_type_obj ?: ( $post_type ? get_post_type_object( $post_type ) : null );
475 if ( ! $breadcrumb_pto ) {
476 return false;
477 }
478 $url = ! empty( $breadcrumb_pto->has_archive ) ? get_post_type_archive_link( $post_type ) : false;
479 $this->add_item( $breadcrumb_items, $breadcrumb_pto->labels->name, $url );
480 return true;
481 }
482
483 /**
484 * Add term ancestors (linked).
485 */
486 private function add_term_ancestors( array &$breadcrumb_items, $term ) : void {
487 foreach ( array_reverse( get_ancestors( $term->term_id, $term->taxonomy, 'taxonomy' ) ) as $id ) {
488 $this->add_term_item( $breadcrumb_items, get_term( $id, $term->taxonomy ) );
489 }
490 }
491
492 /**
493 * Append taxonomy trail (category + tag when available) to breadcrumb items.
494 * Returns true if any taxonomy items were appended.
495 */
496 private function append_taxonomy_trail( array &$breadcrumb_items, int $post_id, string $post_type, string $preferred_tax = '' ) : bool {
497 $breadcrumb_tax = $this->preferred_tax( $post_type, $preferred_tax );
498 if ( ! $breadcrumb_tax ) {
499 return false;
500 }
501
502 $secondary = ( $preferred_tax === '' ) ? $this->secondary_tax( $post_type, $breadcrumb_tax ) : '';
503 $taxes = array_filter( array_unique( [ $breadcrumb_tax, $secondary ] ) );
504 $seen_terms = [];
505 foreach ( $taxes as $taxonomy ) {
506 $primary = $this->primary_term( $post_id, $taxonomy );
507 if ( ! $primary ) {
508 continue;
509 }
510
511 foreach ( array_reverse( get_ancestors( $primary->term_id, $primary->taxonomy, 'taxonomy' ) ) as $id ) {
512 $breadcrumb_t = get_term( $id, $primary->taxonomy );
513 $key = $breadcrumb_t ? $breadcrumb_t->taxonomy . ':' . $breadcrumb_t->term_id : '';
514 if ( ! $breadcrumb_t || is_wp_error( $breadcrumb_t ) || isset( $seen_terms[ $key ] ) ) {
515 continue;
516 }
517 $seen_terms[ $key ] = true;
518 $this->add_term_item( $breadcrumb_items, $breadcrumb_t );
519 }
520 $key = $primary->taxonomy . ':' . $primary->term_id;
521 if ( ! isset( $seen_terms[ $key ] ) ) {
522 $seen_terms[ $key ] = true;
523 $this->add_term_item( $breadcrumb_items, $primary );
524 }
525 }//end foreach
526 return ! empty( $seen_terms );
527 }
528
529 /**
530 * Preferred taxonomy
531 */
532 private function preferred_tax( $post_type, $preferred = '' ) {
533 if ( $preferred && taxonomy_exists( $preferred ) ) {
534 return $preferred;
535 }
536 $taxes = get_object_taxonomies( $post_type, 'objects' );
537 foreach ( $taxes as $breadcrumb_t ) {
538 if ( $breadcrumb_t->public && $breadcrumb_t->hierarchical ) {
539 return $breadcrumb_t->name;
540 }
541 }
542 foreach ( $taxes as $breadcrumb_t ) {
543 if ( $breadcrumb_t->public ) {
544 return $breadcrumb_t->name;
545 }
546 }
547 return false;
548 }
549
550 /**
551 * Secondary taxonomy (first public non-hierarchical) when primary is hierarchical.
552 */
553 private function secondary_tax( $post_type, $primary_tax = '' ) {
554 $primary = $primary_tax ? get_taxonomy( $primary_tax ) : null;
555 if ( $primary && empty( $primary->hierarchical ) ) {
556 return false;
557 }
558
559 foreach ( get_object_taxonomies( $post_type, 'objects' ) as $breadcrumb_t ) {
560 if ( $breadcrumb_t->public && ! $breadcrumb_t->hierarchical && $breadcrumb_t->name !== $primary_tax ) {
561 return $breadcrumb_t->name;
562 }
563 }
564 return false;
565 }
566
567 /**
568 * Get a representative post type for a taxonomy.
569 */
570 private function taxonomy_post_type( $taxonomy ) {
571 $tax = $taxonomy ? get_taxonomy( $taxonomy ) : null;
572 if ( ! $tax || empty( $tax->object_type ) ) {
573 return false;
574 }
575 $object_types = (array) $tax->object_type;
576 if ( in_array( 'listing', $object_types, true ) ) {
577 return 'listing';
578 }
579 return reset( $object_types );
580 }
581
582 /**
583 * Build taxonomy archive items: Post Type > Term ancestors > Term (linked).
584 */
585 private function build_taxonomy_archive_items( array $breadcrumb_items, $term ) : array {
586 $breadcrumb_pt = $this->taxonomy_post_type( $term->taxonomy );
587 $breadcrumb_pto = $breadcrumb_pt ? get_post_type_object( $breadcrumb_pt ) : null;
588 $this->add_post_type_item( $breadcrumb_items, $breadcrumb_pt, $breadcrumb_pto );
589
590 $this->add_term_ancestors( $breadcrumb_items, $term );
591 $this->add_term_item( $breadcrumb_items, $term );
592
593 return $breadcrumb_items;
594 }
595
596 /**
597 * Primary term (Yoast) or first term
598 */
599 private function primary_term( $post_id, $taxonomy ) {
600 if ( class_exists( 'WPSEO_Primary_Term' ) ) {
601 $yoast = new \WPSEO_Primary_Term( $taxonomy, $post_id );
602 $id = (int) $yoast->get_primary_term();
603 if ( $id ) {
604 $breadcrumb_t = get_term( $id, $taxonomy );
605 if ( $breadcrumb_t && ! is_wp_error( $breadcrumb_t ) ) {
606 return $breadcrumb_t;
607 }
608 }
609 }
610 $terms = get_the_terms( $post_id, $taxonomy );
611 return ( $terms && ! is_wp_error( $terms ) ) ? array_shift( $terms ) : null;
612 }
613
614 /**
615 * Tail limit
616 */
617 private function limit_items( array $breadcrumb_items, int $max ) {
618 if ( $max > 0 && count( $breadcrumb_items ) > $max ) {
619 return array_slice( $breadcrumb_items, - $max );
620 }
621 return $breadcrumb_items;
622 }
623
624 /**
625 * Best-effort current request URL for schema item fallback.
626 */
627 private function current_request_url() : string {
628 if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
629 $request_uri = wp_unslash( $_SERVER['REQUEST_URI'] );
630 $request_uri = esc_url_raw( $request_uri );
631
632 return home_url( $request_uri );
633 }
634
635 $permalink = get_permalink();
636 if ( ! empty( $permalink ) ) {
637 return $permalink;
638 }
639
640 return home_url( '/' );
641 }
642
643 /**
644 * Breadcrumbs-style HTML (matches Breadcrumbs classes; includes microdata)
645 */
646 private function render_html_breadcrumbs( array $breadcrumb_items, array $breadcrumb_normalize ) {
647 $enable_schema = ! empty( $breadcrumb_normalize['enableFaqSchema'] );
648 ob_start();
649 ?>
650 <div aria-label="<?php esc_attr_e( 'breadcrumb', 'ablocks' ); ?>"
651 class="ablocks-breadcrumbs"
652 role="navigation"
653 <?php if ( $enable_schema ) : ?>
654 itemscope
655 itemtype="https://schema.org/BreadcrumbList"
656 <?php endif; ?>>
657
658 <?php
659
660 if ( $breadcrumb_normalize['beforeTextImage'] == 'true' ) : ?>
661 <?php if ( $breadcrumb_normalize['beforeBreadcrumbTextImage'] == 'text' ) : ?>
662 <span class="ablocks-breadcrumbs__before-text ablocks-breadcrumbs__item">
663 <?php echo esc_html( $breadcrumb_normalize['beforeBreadcrumbText'] ); ?>
664 </span>
665 <?php if ( $breadcrumb_normalize['beforeSeparator'] == 'true' ) : ?>
666 <span class="ablocks-breadcrumbs__separator"><?php echo esc_html( $breadcrumb_normalize['separator'] ); ?></span>
667 <?php endif; ?>
668 <?php elseif ( $breadcrumb_normalize['beforeBreadcrumbTextImage'] == 'image' ) : ?>
669 <span class="ablocks-breadcrumbs___before-image"><img src="<?php echo esc_url( $breadcrumb_normalize['before_breadcrumb_image'] ); ?>" alt="">
670 </span>
671 <?php if ( $breadcrumb_normalize['beforeSeparator'] == 'true' ) : ?>
672 <span class="ablocks-breadcrumbs__separator"><?php echo esc_html( $breadcrumb_normalize['separator'] ); ?></span>
673 <?php endif; ?>
674 <?php endif; ?>
675 <?php endif; ?>
676
677 <?php foreach ( $breadcrumb_items as $i => $breadcrumb_it ) : ?>
678 <span class="ablocks-breadcrumbs__item"
679 <?php if ( $enable_schema ) : ?>
680 itemprop="itemListElement"
681 itemscope
682 itemtype="https://schema.org/ListItem"
683 <?php endif; ?>>
684
685 <?php if ( ! empty( $breadcrumb_it['url'] ) ) : ?>
686 <a <?php if ( $enable_schema ) : ?>itemprop="item"<?php endif; ?> href="<?php echo esc_url( $breadcrumb_it['url'] ); ?>">
687 <span <?php if ( $enable_schema ) : ?>itemprop="name"<?php endif; ?>><?php echo esc_html( $breadcrumb_it['label'] ); ?></span>
688 </a>
689 <?php else : ?>
690 <span class="ablocks-breadcrumbs__item-current" <?php if ( $enable_schema ) : ?>itemprop="name"<?php endif; ?>><?php echo esc_html( $breadcrumb_it['label'] ); ?></span>
691 <?php $current_item_url = $this->current_request_url(); ?>
692 <?php if ( $enable_schema && ! empty( $current_item_url ) ) : ?>
693 <meta itemprop="item" content="<?php echo esc_url( $current_item_url ); ?>" />
694 <?php endif; ?>
695 <?php endif; ?>
696 <?php if ( $enable_schema ) : ?>
697 <meta itemprop="position" content="<?php echo (int) ( $i + 1 ); ?>" />
698 <?php endif; ?>
699 </span>
700 <?php if ( $i < count( $breadcrumb_items ) - 1 ) : ?>
701 <span class="ablocks-breadcrumbs__separator"><?php echo esc_html( $breadcrumb_normalize['separator'] ); ?></span>
702 <?php endif; ?>
703 <?php endforeach; ?>
704 </div>
705 <?php
706 return trim( ob_get_clean() );
707 }
708
709
710 /**
711 * Block → PHP SSR entry (use in render_callback)
712 */
713 public function render_block_content( $attributes, $content = '', $block_instance = null ) {
714 $breadcrumb_normalize = $this->normalize( $attributes );
715
716 // --- Ensure correct $post in editor/preview (REST) ---
717 $restore_post = null;
718
719 if ( $block_instance && ! empty( $block_instance->context['postId'] ) ) {
720 $maybe = get_post( (int) $block_instance->context['postId'] );
721
722 if ( $maybe instanceof \WP_Post ) {
723 global $post;
724 $restore_post = $post ?? null;
725 $post = $maybe;
726 setup_postdata( $post );
727 }
728 }
729
730 $breadcrumb_items = $this->build_items( $breadcrumb_normalize );
731
732 if ( $restore_post !== null ) {
733 wp_reset_postdata();
734 global $post;
735 $post = $restore_post;
736 }
737
738 if ( empty( $breadcrumb_items ) ) {
739 return '';
740 }
741 $breadcrumb_items = $this->limit_items( $breadcrumb_items, (int) $breadcrumb_normalize['max'] );
742
743 return $this->render_html_breadcrumbs( $breadcrumb_items, $breadcrumb_normalize );
744 }
745
746
747 }
748