PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.6.6
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.6.6
2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.0 2.3.3 2.3.2 2.3.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 All 128 releases
wdesignkit / includes / admin / class-nexter-block-render.php

class-nexter-block-render.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow 2.6.6, at includes/admin/class-nexter-block-render.php

626 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit;
3
4 /**
5 * WDKIT_Block_Processor class
6 *
7 * @package WDKIT
8 * @subpackage WDKIT_Block_Processor
9 * @since 2.0.0
10 */
11 class WDKIT_Nexter_Block_Processor {
12
13 /**
14 * Run the block processor
15 *
16 * @param array $blocks
17 * @return array
18 */
19 public function run( $blocks ) {
20 return $this->process_blocks( $blocks );
21 }
22
23 /**
24 * Process blocks
25 *
26 * @param array $blocks
27 * @return array
28 */
29 private function process_blocks( $blocks ) {
30
31 foreach ( $blocks as &$block ) {
32
33 $name = $block['blockName'] ?? '';
34 $attrs = $block['attrs'] ?? [];
35
36 switch ( $name ) {
37
38 case 'tpgb/tp-accordion':
39 $block = $this->process_accordion( $block, $attrs );
40 break;
41
42 case 'tpgb/tp-accordion-inner':
43 $block = $this->process_accordion_inner( $block, $attrs );
44 break;
45
46 case 'tpgb/tp-blockquote':
47 $block = $this->process_blockquote( $block, $attrs );
48 break;
49
50 case 'tpgb/tp-heading':
51 $block = $this->process_heading( $block, $attrs );
52 break;
53
54 case 'tpgb/tp-pro-paragraph':
55 $block = $this->process_pro_paragraph( $block, $attrs );
56 break;
57
58 case 'tpgb/tp-button-core':
59 $block = $this->process_button_core( $block, $attrs );
60 break;
61
62 case 'tpgb/tp-image':
63 $block = $this->process_image( $block, $attrs );
64 break;
65
66 case 'tpgb/tp-tabs-tours':
67 $block = $this->process_tabs_tours( $block, $attrs );
68 break;
69 }
70
71 if ( ! empty( $block['innerBlocks'] ) ) {
72 $block['innerBlocks'] = $this->process_blocks( $block['innerBlocks'] );
73 }
74 }
75
76 return $blocks;
77 }
78
79 /**
80 * Process accordion block
81 *
82 * @param array $block
83 * @param array $attrs
84 * @return array
85 */
86 private function process_accordion( $block, $attrs ) {
87
88 if ( empty( $attrs['accordianList'] ) || empty( $block['innerHTML'] ) ) {
89 return $block;
90 }
91 $is_editor = isset( $attrs['accorType'] ) && $attrs['accorType'] === 'editor';
92
93 if ( $is_editor ) {
94 return $block;
95 }
96
97 libxml_use_internal_errors(true);
98
99 $dom = new DOMDocument();
100 $dom->loadHTML('<?xml encoding="utf-8" ?>' . $block['innerHTML']);
101 $xpath = new DOMXPath($dom);
102
103 $items = $xpath->query(
104 '//*[contains(concat(" ", normalize-space(@class), " "), " tpgb-accor-item ")]'
105 );
106
107 foreach ($items as $index => $item) {
108
109 if (!isset($attrs['accordianList'][$index])) {
110 continue;
111 }
112
113 $title = wp_kses_post($attrs['accordianList'][$index]['title'] ?? '');
114 $desc = wp_kses_post($attrs['accordianList'][$index]['desc'] ?? '');
115
116 // Update title (ANY TAG with accordion-title class)
117 $titleNode = $xpath->query(
118 './/*[contains(concat(" ", normalize-space(@class), " "), " accordion-title ")]',
119 $item
120 )->item(0);
121
122 if ($titleNode) {
123 $titleNode->nodeValue = $title;
124 }
125
126 // Update description
127 $descNode = $xpath->query(
128 './/*[contains(concat(" ", normalize-space(@class), " "), " tpgb-content-editor ")]',
129 $item
130 )->item(0);
131
132 if ($descNode) {
133 $descNode->nodeValue = $desc;
134 }
135 }
136
137 $new_html = $dom->saveHTML($dom->getElementsByTagName('body')->item(0));
138 $new_html = preg_replace('/^<body>|<\/body>$/', '', $new_html);
139
140 $block['innerHTML'] = $new_html;
141 $block['innerContent'][0] = $new_html;
142
143 return $block;
144 }
145
146 private function process_accordion_inner( $block, $attrs ) {
147
148 if ( empty( $block['innerContent'] ) ) {
149 return $block;
150 }
151
152 $is_editor = isset( $attrs['contentType'] ) && $attrs['contentType'] === 'editor';
153
154 foreach ( $block['innerContent'] as $i => $chunk ) {
155
156 if ( $chunk === null ) {
157 continue;
158 }
159
160 // Update title
161 if ( ! empty( $attrs['title'] ) && strpos( $chunk, 'accordion-title' ) !== false ) {
162 $chunk = preg_replace_callback(
163 '/(<([a-z][a-z0-9]*)\b[^>]*?class="[^"]*\baccordion-title\b[^"]*"[^>]*?>)([^<]*)(<\/\2>)/si',
164 function( $m ) use ( $attrs ) {
165 return $m[1] . esc_html( $attrs['title'] ) . $m[4];
166 },
167 $chunk,
168 1
169 );
170 }
171
172 // Skip desc replacement entirely when contentType = editor
173 // because tpgb-content-editor contains inner blocks split across innerContent
174 if ( ! $is_editor && ! empty( $attrs['desc'] ) && strpos( $chunk, 'tpgb-content-editor' ) !== false ) {
175 $chunk = preg_replace(
176 '/(<div\b[^>]*class="[^"]*\btpgb-content-editor\b[^"]*"[^>]*>)([\s\S]*?)(<\/div>)/i',
177 '$1' . wp_kses_post( $attrs['desc'] ) . '$3',
178 $chunk,
179 1
180 );
181 }
182
183 $block['innerContent'][ $i ] = $chunk;
184 }
185
186 return $block;
187 }
188
189 /**
190 * Process blockquote block
191 *
192 * @param array $block
193 * @param array $attrs
194 * @return array
195 */
196 private function process_blockquote( $block, $attrs ) {
197
198 if ( empty( $block['innerHTML'] ) ) {
199 return $block;
200 }
201
202 $content = wp_kses_post( $attrs['content'] ?? '' );
203 $author = wp_kses_post( $attrs['authorName'] ?? '' );
204
205 $html = $block['innerHTML'];
206
207 // Update quote text
208 if ( $content ) {
209
210 $html = preg_replace(
211 '/(<span[^>]*class="[^"]*quote-text[^"]*"[^>]*>)([\s\S]*?)(<\/span>)/',
212 '$1' . $content . '$3',
213 $html,
214 1
215 );
216
217 $block['attrs']['content'] = $content;
218 }
219
220 // Update author name
221 if ( $author ) {
222
223 $html = preg_replace(
224 '/(<div[^>]*class="[^"]*tpgb-quote-author[^"]*"[^>]*>)([\s\S]*?)(<\/div>)/',
225 '$1' . $author . '$3',
226 $html,
227 1
228 );
229
230 $block['attrs']['authorName'] = $author;
231 }
232
233 $block['innerHTML'] = $html;
234 $block['innerContent'][0] = $html;
235
236 return $block;
237 }
238
239 /**
240 * Process heading block
241 *
242 * @param array $block
243 * @param array $attrs
244 * @return array
245 */
246 private function process_heading( $block, $attrs ) {
247
248 // Take exTitle and map it to title
249 if ( empty( $attrs['exTitle'] ) ) {
250 return $block;
251 }
252
253 $new_title = wp_kses_post( $attrs['exTitle'] );
254
255 if ( empty( $block['innerHTML'] ) ) {
256 return $block;
257 }
258
259 libxml_use_internal_errors(true);
260
261 $dom = new DOMDocument();
262 $dom->loadHTML('<?xml encoding="utf-8" ?>' . $block['innerHTML']);
263 $xpath = new DOMXPath($dom);
264
265 // Find element having tp-core-heading class
266 $heading_node = $xpath->query(
267 '//*[contains(concat(" ", normalize-space(@class), " "), " tp-core-heading ")]'
268 )->item(0);
269
270 if ( $heading_node ) {
271 $heading_node->nodeValue = $new_title;
272 }
273
274 // Clean HTML (remove body wrapper)
275 $new_html = $dom->saveHTML($dom->getElementsByTagName('body')->item(0));
276 $new_html = preg_replace('/^<body>|<\/body>$/', '', $new_html);
277
278 // Update block HTML
279 $block['innerHTML'] = $new_html;
280 $block['innerContent'][0] = $new_html;
281
282 // Also update attribute for editor sync
283 $block['attrs']['title'] = $new_title;
284 $block['attrs']['exTitle'] = $new_title;
285
286 return $block;
287 }
288
289 /**
290 * Process pro paragraph block
291 *
292 * @param array $block
293 * @param array $attrs
294 * @return array
295 */
296 private function process_pro_paragraph( $block, $attrs ) {
297
298 if ( empty( $block['innerHTML'] ) ) {
299 return $block;
300 }
301
302 $new_title = ! empty( $attrs['exTitle'] ) ? wp_kses_post( $attrs['exTitle'] ) : '';
303 $new_content = ! empty( $attrs['exproCnt'] ) ? wp_kses_post( $attrs['exproCnt'] ) : '';
304
305 $title_tag = ! empty( $attrs['titleTag'] ) ? strtolower( $attrs['titleTag'] ) : 'h3';
306 $desc_tag = ! empty( $attrs['descTag'] ) ? strtolower( $attrs['descTag'] ) : 'p';
307
308 libxml_use_internal_errors(true);
309
310 $dom = new DOMDocument();
311 $dom->loadHTML('<?xml encoding="utf-8" ?>' . $block['innerHTML']);
312 $xpath = new DOMXPath($dom);
313
314 /*
315 |--------------------------------------------------------------------------
316 | 🔹 Update Title
317 |--------------------------------------------------------------------------
318 */
319 if ( $new_title ) {
320
321 $title_node = $xpath->query(
322 '//*[contains(concat(" ", normalize-space(@class), " "), " pro-heading-inner ")]'
323 )->item(0);
324
325 if ( $title_node ) {
326
327 // If tag is different, replace tag but keep class
328 if ( strtolower($title_node->nodeName) !== $title_tag ) {
329
330 $new_node = $dom->createElement($title_tag);
331 $new_node->setAttribute('class', $title_node->getAttribute('class'));
332 $new_node->nodeValue = $new_title;
333
334 $title_node->parentNode->replaceChild($new_node, $title_node);
335
336 } else {
337 $title_node->nodeValue = $new_title;
338 }
339 }
340
341 $block['attrs']['title'] = $new_title;
342 $block['attrs']['exTitle'] = $new_title;
343 }
344
345 /*
346 |--------------------------------------------------------------------------
347 | 🔹 Update Description
348 |--------------------------------------------------------------------------
349 */
350 if ( $new_content ) {
351
352 $desc_node = $xpath->query(
353 '//*[contains(concat(" ", normalize-space(@class), " "), " pro-paragraph-inner ")]/*'
354 )->item(0);
355
356 if ( $desc_node ) {
357
358 if ( strtolower($desc_node->nodeName) !== $desc_tag ) {
359
360 $new_node = $dom->createElement($desc_tag);
361 $new_node->nodeValue = $new_content;
362
363 $desc_node->parentNode->replaceChild($new_node, $desc_node);
364
365 } else {
366 $desc_node->nodeValue = $new_content;
367 }
368 }
369
370 $block['attrs']['content'] = $new_content;
371 $block['attrs']['exproCnt'] = $new_content;
372 }
373
374 /*
375 |--------------------------------------------------------------------------
376 | 🔹 Clean HTML
377 |--------------------------------------------------------------------------
378 */
379 $new_html = $dom->saveHTML($dom->getElementsByTagName('body')->item(0));
380 $new_html = preg_replace('/^<body>|<\/body>$/', '', $new_html);
381
382 $block['innerHTML'] = $new_html;
383 $block['innerContent'][0] = $new_html;
384
385 return $block;
386 }
387
388 /**
389 * Process button core block
390 *
391 * @param array $block
392 * @param array $attrs
393 * @return array
394 */
395 private function process_button_core( $block, $attrs ) {
396
397 if ( empty( $block['innerHTML'] ) ) {
398 return $block;
399 }
400
401 $new_text = ! empty( $attrs['exbtxt'] ) ? wp_kses_post( $attrs['exbtxt'] ): '';
402
403 $new_link = ! empty( $attrs['bLink']['url'] ) ? esc_url( $attrs['bLink']['url'] ) : '';
404
405 libxml_use_internal_errors(true);
406
407 $dom = new DOMDocument();
408 $dom->loadHTML('<?xml encoding="utf-8" ?>' . $block['innerHTML']);
409 $xpath = new DOMXPath($dom);
410
411
412 if ( $new_text ) {
413
414 $text_node = $xpath->query(
415 '//*[contains(concat(" ", normalize-space(@class), " "), " tpgb-btn-txt ")]'
416 )->item(0);
417
418 if ( $text_node ) {
419 $text_node->nodeValue = $new_text;
420 }
421
422 $block['attrs']['btxt'] = $new_text;
423 $block['attrs']['exbtxt'] = $new_text;
424 }
425
426 if ( $new_link ) {
427
428 $link_node = $xpath->query('//a[contains(@class,"tpgb-btn-link")]')->item(0);
429
430 if ( $link_node ) {
431 $link_node->setAttribute('href', $new_link);
432 }
433
434 $block['attrs']['bLink'] = $new_link;
435 }
436
437
438 $new_html = $dom->saveHTML($dom->getElementsByTagName('body')->item(0));
439 $new_html = preg_replace('/^<body>|<\/body>$/', '', $new_html);
440
441 $block['innerHTML'] = $new_html;
442 $block['innerContent'][0] = $new_html;
443
444 return $block;
445 }
446
447 /**
448 * Process image block
449 *
450 * @param array $block
451 * @param array $attrs
452 * @return array
453 */
454 private function process_image( $block, $attrs ) {
455
456 if ( empty( $block['innerHTML'] ) ) {
457 return $block;
458 }
459
460 $new_img = ! empty( $attrs['tImg']['url'] )
461 ? esc_url( $attrs['tImg']['url'] )
462 : '';
463
464 $new_alt = isset( $attrs['tImg']['alt'] )
465 ? wp_kses_post( $attrs['tImg']['alt'] )
466 : '';
467
468 $new_id = ! empty( $attrs['tImg']['id'] )
469 ? intval( $attrs['tImg']['id'] )
470 : '';
471
472 $new_link = ! empty( $attrs['tiLink']['url'] )
473 ? esc_url( $attrs['tiLink']['url'] )
474 : '';
475
476 libxml_use_internal_errors(true);
477
478 $dom = new DOMDocument();
479 $dom->loadHTML('<?xml encoding="utf-8" ?>' . $block['innerHTML']);
480 $xpath = new DOMXPath($dom);
481
482 /*
483 |--------------------------------------------------------------------------
484 | 🔹 Update <img>
485 |--------------------------------------------------------------------------
486 */
487 $img_node = $xpath->query('//img[contains(@class,"tpgb-img-inner")]')->item(0);
488
489 if ( $img_node ) {
490
491 if ( $new_img ) {
492 $img_node->setAttribute('src', $new_img);
493 $block['attrs']['tImg']['url'] = $new_img;
494 }
495
496 if ( $new_alt ) {
497 $img_node->setAttribute('alt', $new_alt);
498 $block['attrs']['tImg']['alt'] = $new_alt;
499 }
500
501 if ( $new_id ) {
502 $img_node->setAttribute('class', 'tpgb-img-inner wp-image-' . $new_id);
503 $block['attrs']['tImg']['id'] = $new_id;
504 }
505 }
506
507 /*
508 |--------------------------------------------------------------------------
509 | 🔹 Update Link
510 |--------------------------------------------------------------------------
511 */
512 if ( $new_link ) {
513
514 $link_node = $xpath->query('//a')->item(0);
515
516 if ( $link_node ) {
517 $link_node->setAttribute('href', $new_link);
518 }
519
520 $block['attrs']['tiLink']['url'] = $new_link;
521 }
522
523 /*
524 |--------------------------------------------------------------------------
525 | 🔹 Clean HTML
526 |--------------------------------------------------------------------------
527 */
528 $new_html = $dom->saveHTML($dom->getElementsByTagName('body')->item(0));
529 $new_html = preg_replace('/^<body>|<\/body>$/', '', $new_html);
530
531 $block['innerHTML'] = $new_html;
532 $block['innerContent'][0] = $new_html;
533
534 return $block;
535 }
536
537 /**
538 * Process tabs tours block
539 *
540 * @param array $block
541 * @param array $attrs
542 * @return array
543 */
544
545 private function process_tabs_tours( $block, $attrs ) {
546
547 if ( empty( $attrs['tablistRepeater'] ) ) {
548 return $block;
549 }
550
551 $is_editor = isset( $attrs['tabType'] ) && $attrs['tabType'] === 'editor';
552
553 foreach ( $attrs['tablistRepeater'] as $index => $item ) {
554
555 $tab_number = $index + 1;
556 $title = sanitize_text_field( $item['tabTitle'] ?? '' );
557 $desc = wp_kses_post( $item['tabDescription'] ?? '' );
558
559 /*
560 |--------------------------------------------------------------------------
561 | Update Desktop Nav Title — innerContent[0] only
562 |--------------------------------------------------------------------------
563 */
564 if ( isset( $block['innerContent'][0] ) ) {
565 $block['innerContent'][0] = preg_replace(
566 '/(<div[^>]*class="[^"]*tpgb-tab-header[^"]*"[^>]*data-tab="' . $tab_number . '"[^>]*>\s*<span>)(.*?)(<\/span>)/s',
567 '$1' . esc_html( $title ) . '$3',
568 $block['innerContent'][0],
569 1
570 );
571 }
572
573 /*
574 |--------------------------------------------------------------------------
575 | Sync Title in attrs
576 |--------------------------------------------------------------------------
577 */
578 $block['attrs']['tablistRepeater'][ $index ]['tabTitle'] = $title;
579
580 /*
581 |--------------------------------------------------------------------------
582 | Description mode only — NOT editor
583 |--------------------------------------------------------------------------
584 */
585 if ( ! $is_editor && ! empty( $desc ) ) {
586
587 /*
588 |----------------------------------------------------------------------
589 | Update Mobile Title — innerContent[0] only
590 |----------------------------------------------------------------------
591 */
592 if ( isset( $block['innerContent'][0] ) ) {
593 $block['innerContent'][0] = preg_replace(
594 '/(<div[^>]*class="[^"]*tab-mobile-title[^"]*"[^>]*data-tab="' . $tab_number . '"[^>]*>\s*<span>)(.*?)(<\/span>)/s',
595 '$1' . esc_html( $title ) . '$3',
596 $block['innerContent'][0],
597 1
598 );
599 }
600
601 /*
602 |----------------------------------------------------------------------
603 | Update Description — innerContent[0] only
604 |----------------------------------------------------------------------
605 */
606 if ( isset( $block['innerContent'][0] ) ) {
607 $block['innerContent'][0] = preg_replace(
608 '/(<div[^>]*class="[^"]*tpgb-tab-content[^"]*"[^>]*data-tab="' . $tab_number . '"[^>]*>.*?<div[^>]*class="[^"]*tpgb-content-editor[^"]*"[^>]*>)(.*?)(<\/div>\s*<\/div>)/s',
609 '$1' . $desc . '$3',
610 $block['innerContent'][0],
611 1
612 );
613 }
614
615 /*
616 |----------------------------------------------------------------------
617 | Sync Description in attrs
618 |----------------------------------------------------------------------
619 */
620 $block['attrs']['tablistRepeater'][ $index ]['tabDescription'] = $desc;
621 }
622 }
623
624 return $block;
625 }
626 }