PluginProbe
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables / 2.3.0
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables v2.3.0
2.3.0 2.2.14 2.2.13 2.2.12 2.2.11 2.2.10 2.2.9 2.2.8 2.2.7 2.2.6 2.2.5 2.2.4 2.2.3 trunk 1.0.0 1.0.1 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2
← All changes | includes/Helpers/Utils.php +1432 -958 2.1.1 → 2.3.0 View file →
@@ -1,958 +1,1432 @@
1 -<?php
2 -
3 -namespace TableBuilder\Helpers;
4 -
5 -defined( 'ABSPATH' ) || exit;
6 -
7 -/**
8 - * Global helper class.
9 - *
10 - * @since 1.0.0
11 - */
12 -
13 -class Utils {
14 -
15 - /**
16 - * Returns an array of allowed HTML tags and attributes for SVG elements.
17 - *
18 - * @return array The array of allowed HTML tags and attributes.
19 - */
20 - public static function svg_allowed_html() {
21 - $allowed_svg_tags = [
22 - 'svg' => [
23 - 'xmlns' => true,
24 - 'width' => true,
25 - 'height' => true,
26 - 'viewBox' => true,
27 - 'viewbox' => true,
28 - 'fill' => true,
29 - 'class' => true,
30 - 'aria-hidden' => true,
31 - 'aria-labelledby' => true,
32 - 'role' => true,
33 - 'preserveaspectratio' => true,
34 - 'version' => true,
35 - ],
36 - 'title' => array( 'title' => true ),
37 - 'g' => [
38 - 'transform' => true,
39 - 'style' => true,
40 - 'id' => true,
41 - ],
42 - 'path' => [
43 - 'd' => true,
44 - 'fill' => true,
45 - 'fill-rule' => true,
46 - 'transform' => true,
47 - 'style' => true,
48 - 'opacity' => true,
49 - 'stroke' => true,
50 - 'stroke-width' => true,
51 - 'stroke-miterlimit' => true,
52 - 'stroke-linecap' => true,
53 - 'stroke-linejoin' => true,
54 - 'fill-opacity' => true,
55 - ],
56 - 'circle' => [
57 - 'cx' => true,
58 - 'cy' => true,
59 - 'r' => true,
60 - 'fill' => true,
61 - 'stroke' => true,
62 - 'stroke-width' => true,
63 - ],
64 - 'ellipse' => [
65 - 'cx' => true,
66 - 'cy' => true,
67 - 'rx' => true,
68 - 'ry' => true,
69 - 'fill' => true,
70 - 'stroke' => true,
71 - 'stroke-width' => true,
72 - ],
73 - 'line' => [
74 - 'x1' => true,
75 - 'y1' => true,
76 - 'x2' => true,
77 - 'y2' => true,
78 - 'stroke' => true,
79 - 'stroke-width' => true,
80 - ],
81 - 'polygon' => [
82 - 'points' => true,
83 - 'fill' => true,
84 - 'stroke' => true,
85 - 'stroke-width' => true,
86 - ],
87 - 'polyline' => [
88 - 'points' => true,
89 - 'fill' => true,
90 - 'stroke' => true,
91 - 'stroke-width' => true,
92 - ],
93 - 'rect' => [
94 - 'x' => true,
95 - 'y' => true,
96 - 'width' => true,
97 - 'height' => true,
98 - 'fill' => true,
99 - 'stroke' => true,
100 - 'stroke-width' => true,
101 - ],
102 - 'text' => [
103 - 'x' => true,
104 - 'y' => true,
105 - 'dx' => true,
106 - 'dy' => true,
107 - 'text-anchor' => true,
108 - 'style' => true,
109 - ],
110 - 'tspan' => [
111 - 'x' => true,
112 - 'y' => true,
113 - 'dx' => true,
114 - 'dy' => true,
115 - 'text-anchor' => true,
116 - 'style' => true,
117 - ],
118 - 'defs' => [],
119 - 'lineargradient' => [
120 - 'id' => true,
121 - 'x1' => true,
122 - 'y1' => true,
123 - 'x2' => true,
124 - 'y2' => true,
125 - 'gradientunits' => true,
126 - ],
127 - 'stop' => [
128 - 'offset' => true,
129 - 'style' => true,
130 - 'stop-color' => true,
131 - 'stop-opacity' => true,
132 - ],
133 - 'radialgradient' => [
134 - 'id' => true,
135 - 'cx' => true,
136 - 'cy' => true,
137 - 'r' => true,
138 - 'gradientunits' => true,
139 - 'gradienttransform' => true,
140 - ],
141 - ];
142 -
143 - return apply_filters( 'table_builder_allowed_svg_attrs_tags', $allowed_svg_tags );
144 - }
145 -
146 - /**
147 - * Returns an array of allowed JSON attribute tags.
148 - *
149 - * This function defines an array of allowed JSON attribute tags and their corresponding properties.
150 - * The array includes tags such as 'object', 'array', 'string', 'number', 'integer', 'boolean', 'null',
151 - * 'enum', 'const', 'oneOf', 'allOf', 'anyOf', 'not', 'if', 'then', 'else', and 'format'.
152 - *
153 - * @return array The array of allowed JSON attribute tags.
154 - */
155 - public static function allowed_json_attrs_tags() {
156 - $allowed_json_tags = [
157 - 'object' => [
158 - 'type' => true,
159 - 'properties' => true,
160 - 'required' => true,
161 - 'additionalProperties' => true,
162 - 'propertyNames' => true,
163 - 'dependencies' => true,
164 - 'minProperties' => true,
165 - 'maxProperties' => true,
166 - ],
167 - 'array' => [
168 - 'type' => true,
169 - 'items' => true,
170 - 'minItems' => true,
171 - 'maxItems' => true,
172 - 'uniqueItems' => true,
173 - ],
174 - 'string' => [
175 - 'type' => true,
176 - 'minLength' => true,
177 - 'maxLength' => true,
178 - 'pattern' => true,
179 - 'format' => true,
180 - 'contentEncoding' => true,
181 - 'contentMediaType' => true,
182 - ],
183 - 'number' => [
184 - 'type' => true,
185 - 'minimum' => true,
186 - 'maximum' => true,
187 - 'exclusiveMinimum' => true,
188 - 'exclusiveMaximum' => true,
189 - 'multipleOf' => true,
190 - ],
191 - 'integer' => [
192 - 'type' => true,
193 - 'minimum' => true,
194 - 'maximum' => true,
195 - 'exclusiveMinimum' => true,
196 - 'exclusiveMaximum' => true,
197 - ],
198 - 'boolean' => [
199 - 'type' => true,
200 - ],
201 - 'null' => [
202 - 'type' => true,
203 - ],
204 - 'enum' => [
205 - 'type' => true,
206 - 'enum' => true,
207 - ],
208 - 'const' => [
209 - 'type' => true,
210 - 'const' => true,
211 - ],
212 - 'oneOf' => [
213 - 'type' => true,
214 - 'oneOf' => true,
215 - ],
216 - 'allOf' => [
217 - 'type' => true,
218 - 'allOf' => true,
219 - ],
220 - 'anyOf' => [
221 - 'type' => true,
222 - 'anyOf' => true,
223 - ],
224 - 'not' => [
225 - 'type' => true,
226 - 'not' => true,
227 - ],
228 - 'if' => [
229 - 'type' => true,
230 - 'if' => true,
231 - ],
232 - 'then' => [
233 - 'type' => true,
234 - 'then' => true,
235 - ],
236 - 'else' => [
237 - 'type' => true,
238 - 'else' => true,
239 - ],
240 - 'format' => [
241 - 'type' => true,
242 - 'format' => true,
243 - ],
244 - 'title' => true,
245 - 'description' => true,
246 - 'default' => true,
247 - 'examples' => true,
248 - '$ref' => true,
249 - '$id' => true,
250 - '$schema' => true,
251 - // Adding Lottie-specific keys
252 - 'v' => true,
253 - 'meta' => true,
254 - 'fr' => true,
255 - 'ip' => true,
256 - 'op' => true,
257 - 'w' => true,
258 - 'h' => true,
259 - 'nm' => true,
260 - 'ddd' => true,
261 - 'assets' => true,
262 - 'layers' => true,
263 - 'markers' => true,
264 - ];
265 -
266 - return apply_filters('table_builder_allowed_json_attrs_tags', $allowed_json_tags);
267 - }
268 -
269 - public static function iframe_allowed_html() {
270 - return array(
271 - 'iframe' => array(
272 - 'src' => true,
273 - 'name' => true,
274 - 'sandbox' => true,
275 - 'width' => true,
276 - 'height' => true,
277 - 'marginheight' => true,
278 - 'marginwidth' => true,
279 - 'scrolling' => true,
280 - 'allowfullscreen' => true,
281 - 'frameborder' => true,
282 - 'title' => true,
283 - 'id' => true,
284 - 'class' => true,
285 - 'style' => true,
286 - 'tabindex' => true,
287 - 'allow' => true,
288 - ),
289 - );
290 - }
291 -
292 - public static function gdc_allowed_html()
293 - {
294 - return array(
295 - 'gdc' => array(
296 - 'selectedpath' => true,
297 - 'class' => true,
298 - 'id' => true,
299 - 'fallback' => true,
300 - 'postcustomfield' => true,
301 - 'postcustomfieldkey' => true,
302 - 'postdatetype' => true,
303 - 'dateformat' => true,
304 - 'customdateformat' => true,
305 - 'excerptlength' => true,
306 - 'tagindex' => true,
307 - 'timetype' => true,
308 - 'timeformat' => true,
309 - 'customtimeformat' => true,
310 - 'categoryindex' => true,
311 - 'nocomment' => true,
312 - 'singlecomment' => true,
313 - 'multicomments' => true,
314 - 'currentdateformat' => true,
315 - 'customcurrentdateformat' => true,
316 - 'currenttimeformat' => true,
317 - 'customcurrenttimeformat' => true,
318 - 'authorinfo' => true,
319 - 'currentuserinfo' => true,
320 - 'acfgroup' => true,
321 - 'acffield' => true,
322 - ),
323 - );
324 - }
325 -
326 - /**
327 - * Returns the allowed HTML tags and attributes for the img element.
328 - *
329 - * @return array The allowed HTML tags and attributes.
330 - */
331 - public static function img_allowed_html() {
332 - return array(
333 - 'img' => array(
334 - 'alt' => true,
335 - 'src' => true,
336 - 'srcset' => true,
337 - 'class' => true,
338 - 'height' => true,
339 - 'width' => true,
340 - )
341 - );
342 - }
343 -
344 - /**
345 - * Returns the allowed HTML tags and attributes for the style element.
346 - *
347 - * @return array The allowed HTML tags and attributes.
348 - */
349 - public static function style_allowed_html() {
350 - return array(
351 - 'style' => array(
352 - 'class' => true,
353 - 'id' => true,
354 - )
355 - );
356 - }
357 -
358 - public static function get_device_list()
359 - {
360 - $default_device_list = [
361 - [
362 - 'label' => 'Desktop',
363 - 'slug' => 'Desktop',
364 - 'value' => 'base',
365 - 'direction' => 'max',
366 - 'isActive' => true,
367 - 'isRequired' => true,
368 - ],
369 - [
370 - 'label' => 'Tablet',
371 - 'slug' => 'Tablet',
372 - 'value' => '1024',
373 - 'direction' => 'max',
374 - 'isActive' => true,
375 - 'isRequired' => true,
376 - ],
377 - [
378 - 'label' => 'Mobile',
379 - 'slug' => 'Mobile',
380 - 'value' => '767',
381 - 'direction' => 'max',
382 - 'isActive' => true,
383 - 'isRequired' => true,
384 - ]
385 - ];
386 -
387 - return $default_device_list;
388 - }
389 -
390 - /**
391 - * Adds class to SVG
392 - *
393 - * @since 1.0.0
394 - * @access public
395 - */
396 - public static function add_class_to_svg( $svg ) {
397 - $originalString = $svg;
398 - $substringToAdd = "class='table-builder-icon' ";
399 -
400 - $position = strpos( $originalString, '<svg' );
401 -
402 - if ( $position !== false ) {
403 - $svg = substr_replace( $originalString, $substringToAdd, $position + 5, 0 );
404 - return $svg;
405 - }
406 -
407 - return $svg;
408 - }
409 -
410 - /**
411 - * Retrieves the dynamic block wrapper attributes.
412 - *
413 - * This function retrieves the wrapper attributes for a dynamic block.
414 - * It checks if the function "get_block_wrapper_attributes" exists and if the block is not empty.
415 - * If both conditions are met, it retrieves the block attributes, including the block ID.
416 - * It then merges the required attributes with any extra attributes provided.
417 - * Finally, it applies the "roxslider/dynamic_block_wrapper_attributes" filter and returns the wrapper attributes.
418 - *
419 - * @param object $block The dynamic block object.
420 - * @param array $extra_attrs Additional attributes to be merged with the required attributes.
421 - * @return string The wrapper attributes for the dynamic block.
422 - */
423 - public static function get_dynamic_block_wrapper_attributes($block, $extra_attrs = array()) {
424 - if(function_exists("get_block_wrapper_attributes") && !empty($block)) {
425 - $block_attrs = $block->attributes;
426 - $block_id = $block_attrs['blockID'];
427 - $required_attrs = array(
428 - 'id' => 'block-' . $block_id
429 - );
430 - $wrapper_attrs = apply_filters('tablebuilder/dynamic_block_wrapper_attributes', array_merge($required_attrs, $extra_attrs), $block);
431 - return get_block_wrapper_attributes($wrapper_attrs);
432 - }
433 - }
434 -
435 - /**
436 - * Extends the allowed HTML tags for post content.
437 - *
438 - * @return array The extended array of allowed HTML tags.
439 - */
440 - public static function post_kses_extend_allowed_html() {
441 - $default_post_allowed_html = wp_kses_allowed_html( 'post' );
442 -
443 - $post_allowed_html = array_merge( $default_post_allowed_html, self::svg_allowed_html(), self::iframe_allowed_html(), self::gdc_allowed_html(), self::style_allowed_html() );
444 -
445 - return $post_allowed_html;
446 - }
447 -
448 - /**
449 - * Retrieves the settings from the specified key in the options table.
450 - *
451 - * @param string $key The key of the settings in the options table.
452 - * @param string $list Optional. The specific list within the settings to retrieve.
453 - * @param string $option Optional. The specific option within the list to retrieve.
454 - * @return mixed The retrieved settings, or false if the list or option is not found.
455 - */
456 - public static function get_settings($key = '', $field = 'status', $inner_field = '' ) {
457 - $settings = get_option( 'table_builder_settings_list' );
458 -
459 - // check if $key & $field both empty
460 - if ( empty($key) && empty($field) ) {
461 - return $settings;
462 - }
463 -
464 - // check for primary key
465 - if ( !empty($key) && !empty($settings[$key]) ) {
466 - $settings = $settings[$key];
467 - }
468 -
469 - // check for primary field
470 - if( !empty($field) ) {
471 - if( $field === 'status' && !empty($settings[$field]) ) {
472 - return ($settings[$field] === 'active') ? true : false;
473 - } else {
474 - $settings = !empty($settings[$field]) ? $settings[$field] : false;
475 - }
476 - }
477 -
478 - // check for inner field
479 - if( !empty($inner_field) ) {
480 - if( isset($settings[$inner_field]['value']) ) {
481 - $settings = $settings[$inner_field]['value'];
482 - } else {
483 - $settings = !empty($settings[$inner_field]) ? $settings[$inner_field] : false;
484 - }
485 - }
486 -
487 - return $settings;
488 - }
489 -
490 - /**
491 - * Returns an array representing the border value based on the given key.
492 - *
493 - * @param mixed $key The key used to determine the border value.
494 - * @return array An array representing the border value. The array contains the following keys:
495 - * - 'border': The border value, or null if the key is null.
496 - */
497 - public static function get_border_value($key) {
498 -
499 -
500 - if (!is_array($key)) {
501 - return ['border' => null];
502 - }
503 -
504 - $keyLength = count($key);
505 -
506 -
507 - if ($keyLength < 3) {
508 - $properties = ['style', 'color', 'width'];
509 - $borderParts = [];
510 -
511 - foreach ($properties as $property) {
512 - if (isset($key[$property])) {
513 - $borderParts[] = $key[$property];
514 - }
515 - }
516 -
517 -
518 - return ['border' => implode(' ', $borderParts)];
519 -
520 - }
521 -
522 -
523 - if ($keyLength === 3) {
524 - if (isset($key['style'])) {
525 - return ['border' => "{$key['width']} {$key['style']} {$key['color']}"];
526 - }
527 - }
528 -
529 - if ($keyLength === 4 || $keyLength === 3) {
530 - $border = [];
531 - foreach ($key as $direction => $value) {
532 - if (isset($value['style'])) {
533 - $border["border-{$direction}"] = "{$value['width']} {$value['style']} {$value['color']}";
534 - }
535 - }
536 - return $border;
537 - }
538 - }
539 -
540 - /**
541 - * get box value
542 - *
543 - * Similar to getBoxValue in roxslider js helper
544 - * @param array $key
545 - * @param string $property
546 - */
547 - public static function get_box_value($key = [], $property = "") {
548 - $top = isset($key['top']) ? $key['top'] : null;
549 - $right = isset($key['right']) ? $key['right'] : null;
550 - $bottom = isset($key['bottom']) ? $key['bottom'] : null;
551 - $left = isset($key['left']) ? $key['left'] : null;
552 -
553 - $boxObject = ['top' => $top, 'right' => $right, 'bottom' => $bottom, 'left' => $left];
554 - $count = count(array_filter($boxObject, function ($value) {
555 - return $value !== null;
556 - }));
557 -
558 - if ($count === 0) return [$property => null];
559 -
560 - if ($count === 4) {
561 - $boxValues = '';
562 -
563 - if ($top === $bottom && $top === $right && $top === $left) {
564 - $boxValues = $top;
565 - } elseif ($top === $bottom && $left === $right) {
566 - $boxValues = "{$top} {$right}";
567 - } else {
568 - $boxValues = "{$top} {$right} {$bottom} {$left}";
569 - }
570 -
571 - return [$property => $boxValues];
572 - }
573 -
574 - $finalBox = [];
575 -
576 - if ($property !== "border-radius") {
577 - foreach ($boxObject as $direction => $value) {
578 - $finalBox["{$property}-{$direction}"] = isset($key[$direction]) ? $key[$direction] : null;
579 - }
580 -
581 - return $finalBox;
582 - }
583 -
584 - if ($property === "border-radius") {
585 - $finalBox["border-top-left-radius"] = $top;
586 - $finalBox["border-top-right-radius"] = $right;
587 - $finalBox["border-bottom-right-radius"] = $bottom;
588 - $finalBox["border-bottom-left-radius"] = $left;
589 -
590 - return $finalBox;
591 - }
592 - }
593 -
594 - /**
595 - * Retrieves the color based on the given type and color.
596 - *
597 - * @param string $type The type of color to retrieve. Can be either 'gradient' or 'color'.
598 - * @param string $color The color value.
599 - * @return string The retrieved color.
600 - */
601 - public static function get_color($type, $color) {
602 -
603 - if (empty($color) || strpos($color, "linear-gradient(") === 0 || strpos($color, "radial-gradient(") === 0 || strpos($color, '#') === 0) {
604 - return $color;
605 - }
606 -
607 - $color_parts = explode(',', $color, 2);
608 -
609 -
610 - $color_parts1 = $color_parts[0];
611 - $color_parts2 = $color_parts[1];
612 -
613 - $bg_color = "";
614 -
615 - if($type == 'gradient'){
616 - $bg_color = "var(--wp--preset--gradient--".$color_parts1.",".$color_parts2.")";
617 -
618 - } else {
619 - $bg_color = "var(--wp--preset--color--".$color_parts1.",". $color_parts2.")";
620 - }
621 - return $bg_color;
622 - }
623 -
624 - /**
625 - * fill_background_generator
626 - *
627 - * Similar to fillBackgroundGenerator in roxSlider js helper
628 - *
629 - * @param array $background
630 - * @param string $device
631 - */
632 - public static function fill_background_generator($background, $device = "Desktop") {
633 -
634 - $fillBackground = [
635 - 'background-image' => '',
636 - ];
637 -
638 - if (isset($background['backgroundType']) && $background['backgroundType'] === 'classic') {
639 - $fillBackground['background-color'] = isset($background['backgroundColor']) ? self::get_color('color', $background['backgroundColor']) : '';
640 - }
641 -
642 - if (isset($background['backgroundType']) && $background['backgroundType'] === 'gradient' && !empty($background['gradient'])) {
643 -
644 - $fillBackground['background-image'] = isset($background['gradient']) ? (self::get_color('gradient',$background['gradient']) ?? '') : '';
645 - }
646 -
647 - if (isset($background['backgroundType']) && $background['backgroundType'] === 'image' && !empty($background['backgroundImage'])) {
648 - if (!empty($background['backgroundImage']['imageUrl'])) {
649 - $fillBackground['background-image'] = "url({$background['backgroundImage']['imageUrl']})";
650 - }
651 -
652 - if (!empty($background['backgroundAttachment'])) {
653 - $fillBackground['background-attachment'] = $background['backgroundAttachment'];
654 - }
655 -
656 - if (!empty($background['backgroundPosition'][$device]) && $background['backgroundPosition'][$device] !== "custom") {
657 - $fillBackground['background-position'] = $background['backgroundPosition'][$device];
658 - }
659 -
660 - if (
661 - !empty($background['backgroundPosition'][$device]) && $background['backgroundPosition'][$device] === "custom" &&
662 - !empty($background['customPositionX'][$device]) && !empty($background['customPositionY'][$device])
663 - ) {
664 - $fillBackground['background-position'] = self::get_slider_value($background['customPositionX'][$device]) . ' ' . self::get_slider_value($background['customPositionY'][$device]);
665 - }
666 -
667 - if (!empty($background['backgroundSize']) && $background['backgroundSize'] !== "custom") {
668 - $fillBackground['background-size'] = $background['backgroundSize'];
669 - }
670 -
671 - if (
672 - !empty($background['backgroundSize']) && $background['backgroundSize'] === "custom" &&
673 - !empty($background['customSize'][$device])
674 - ) {
675 - $fillBackground['background-size'] = self::get_slider_value($background['customSize'][$device]) . ' auto';
676 - }
677 -
678 - if (!empty($background['backgroundRepeat'])) {
679 - $fillBackground['background-repeat'] = $background['backgroundRepeat'];
680 - }
681 - }
682 -
683 - return $fillBackground;
684 - }
685 -
686 - /**
687 - * get_typography_value
688 - *
689 - * Similar to getTypographyValue in roxSlider js helper
690 - */
691 - public static function get_typography_value($key, $device) {
692 - if ($device === 'Desktop') {
693 - return [
694 - 'font-family' => isset($key['fontFamily']['value']) ? $key['fontFamily']['value'] : null,
695 - 'font-size' => isset($key['fontSize'][$device]['size']) && isset($key['fontSize'][$device]['unit'])
696 - ? $key['fontSize'][$device]['size'] . $key['fontSize'][$device]['unit']
697 - : null,
698 - 'font-style' => isset($key['fontStyle']) ? $key['fontStyle'] : null,
699 - 'font-weight' => isset($key['fontWeight']['value']) ? $key['fontWeight']['value'] : null,
700 - 'text-decoration' => isset($key['textDecoration']) ? $key['textDecoration'] : null,
701 - 'text-transform' => isset($key['textTransform']) ? $key['textTransform'] : null,
702 - 'line-height' => isset($key['lineHeight'][$device]['size']) && isset($key['lineHeight'][$device]['unit'])
703 - ? $key['lineHeight'][$device]['size'] . $key['lineHeight'][$device]['unit']
704 - : null,
705 - 'letter-spacing' => isset($key['letterSpacing'][$device]['size']) && isset($key['letterSpacing'][$device]['unit'])
706 - ? $key['letterSpacing'][$device]['size'] . $key['letterSpacing'][$device]['unit']
707 - : null,
708 - 'word-spacing' => isset($key['wordSpacing'][$device]['size']) && isset($key['wordSpacing'][$device]['unit'])
709 - ? $key['wordSpacing'][$device]['size'] . $key['wordSpacing'][$device]['unit']
710 - : null,
711 - ];
712 - } else {
713 - return [
714 - 'font-size' => isset($key['fontSize'][$device]['size']) && isset($key['fontSize'][$device]['unit'])
715 - ? $key['fontSize'][$device]['size'] . $key['fontSize'][$device]['unit']
716 - : null,
717 - 'line-height' => isset($key['lineHeight'][$device]['size']) && isset($key['lineHeight'][$device]['unit'])
718 - ? $key['lineHeight'][$device]['size'] . $key['lineHeight'][$device]['unit']
719 - : null,
720 - 'letter-spacing' => isset($key['letterSpacing'][$device]['size']) && isset($key['letterSpacing'][$device]['unit'])
721 - ? $key['letterSpacing'][$device]['size'] . $key['letterSpacing'][$device]['unit']
722 - : null,
723 - 'word-spacing' => isset($key['wordSpacing'][$device]['size']) && isset($key['wordSpacing'][$device]['unit'])
724 - ? $key['wordSpacing'][$device]['size'] . $key['wordSpacing'][$device]['unit']
725 - : null,
726 - ];
727 - }
728 - }
729 -
730 - /**
731 - * get slider value
732 - *
733 - * Similar to getSliderValue function in JS helper
734 - * @param string $value
735 - * @return string
736 - */
737 - public static function get_slider_value($key) {
738 - $value = '';
739 -
740 - if (!empty($key['size']) && !empty($key['unit'])) {
741 - $value = $key['size'] . $key['unit'];
742 - } elseif (!empty($key['size']) && empty($key['unit'])) {
743 - $value = $key['size'];
744 - } else {
745 - $value = null;
746 - }
747 -
748 - return $value;
749 - }
750 -
751 - /**
752 - * parse css
753 - *
754 - * @param string $css
755 - * @return string
756 - */
757 - public static function parse_css($raw_css) {
758 -
759 - $styles = [];
760 - $device_list = ['desktop', 'tablet', 'mobile', 'tabletlandscape', 'mobilelandscape', 'laptop', 'widescreen'];
761 -
762 - foreach ($device_list as $device) {
763 - $deviceStyles = $raw_css[$device] ?? [];
764 -
765 - $styles[$device] = array_map(function ($style) {
766 - if (!is_array($style) || !isset($style['selector'])) {
767 - return '';
768 - }
769 -
770 - $selector = $style['selector'];
771 - $cssValues = array_filter($style, function ($value, $key) {
772 - return $key !== 'selector' && $value !== null && $value !== '' && !is_numeric($value) && !in_array($value, ['px', 'em', 'rem', '%', 'vh', 'vw']) && strpos($value, 'undefined') === false;
773 - }, ARRAY_FILTER_USE_BOTH);
774 -
775 - if (empty($cssValues)) {
776 - return '';
777 - }
778 -
779 - return "{$selector} { " . implode(' ', array_map(function ($key, $value) {
780 - return "{$key}: {$value};";
781 - }, array_keys($cssValues), $cssValues)) . " }";
782 - }, $deviceStyles);
783 - }
784 -
785 - $device_styles = array_map(function ($style) {
786 - return implode("\n", $style);
787 - }, $styles);
788 -
789 - return $device_styles;
790 - }
791 -
792 - /**
793 - * Check if the block is a roxSlider block.
794 - *
795 - * @param string $attrs
796 - * @return bool
797 - */
798 - public static function is_table_builder_block($block_content, $parsed_block, $attrs = '', $attrs2 = '') {
799 - // Check if $block_content is not empty
800 - $hasBlockContent = !empty($block_content);
801 - // Check if $block['blockName'] is not empty and contains 'roxSlider'
802 - $hasValidBlockName = isset($parsed_block['blockName']) && strpos($parsed_block['blockName'], 'tablebuilder') !== false;
803 -
804 - // Check if $block['attrs']['blockClass'] is not empty
805 - $hasBlockClass = !empty($attrs) && !empty($attrs2)
806 - ? !empty($parsed_block['attrs'][$attrs][$attrs2])
807 - : !empty($parsed_block['attrs'][$attrs] ?? '');
808 -
809 - if(empty($attrs) && empty($attrs2)) {
810 - $hasBlockClass = true;
811 - }
812 -
813 - // Return true if all conditions are met
814 - return $hasBlockContent && $hasValidBlockName && $hasBlockClass;
815 - }
816 -
817 - /**
818 - * Retrieves the link attributes based on the provided attribute array.
819 - *
820 - * @param array $attribute The attribute array containing the link data.
821 - * @return string The generated link attributes as a string.
822 - */
823 - public static function get_link_attributes($attribute) {
824 - if (empty($attribute['url'])) return '';
825 -
826 - $link_data = [];
827 -
828 - $link_data['href'] = esc_url($attribute['url'], wp_allowed_protocols());
829 -
830 - (isset($attribute['newTab']) && $attribute['newTab']) ? $link_data['target'] = '_blank' : '';
831 -
832 - (isset($attribute['noFollow']) && $attribute['noFollow']) ? $link_data['rel'] = "nofollow" : '';
833 -
834 - if (isset($attribute['customAttributes']) && gettype($attribute['customAttributes']) == 'array') {
835 - foreach ($attribute['customAttributes'] as $key => $value) {
836 - if (!empty($value)) {
837 - $attr_key_value = explode('|', $value);
838 -
839 - $attr_key = mb_strtolower($attr_key_value[0]);
840 -
841 - // Not allowed characters are removed.
842 - preg_match('/[-_a-z0-9]+/', $attr_key, $attr_key_matches);
843 -
844 - if (empty($attr_key_matches[0])) {
845 - continue;
846 - }
847 -
848 - $attr_key = $attr_key_matches[0];
849 -
850 - // Javascript events and unescaped href are avoided.
851 - if ('href' === $attr_key || 'on' === substr($attr_key, 0, 2)) {
852 - continue;
853 - }
854 -
855 - if (isset($attr_key_value[1])) {
856 - $attr_value = trim($attr_key_value[1]);
857 - } else {
858 - $attr_value = '';
859 - }
860 -
861 - $link_data[$attr_key] = $attr_value;
862 - }
863 - }
864 - }
865 -
866 - $link_attributes = '';
867 - foreach ($link_data as $key => $value) {
868 - $link_attributes .= sprintf('%s="%s" ', $key, esc_attr($value));
869 - }
870 -
871 - return $link_attributes;
872 - }
873 -
874 - public static function status() {
875 -
876 - $cached = wp_cache_get('table_builder__license_status');
877 -
878 - if(false !== $cached) {
879 - return $cached;
880 - }
881 -
882 - $oppai = get_option('__table_builder_oppai__', '');
883 - $key = get_option('__table_builder_license_key__', '');
884 - $status = 'invalid';
885 -
886 - if($oppai != '' && $key != '') {
887 - $status = 'valid';
888 - }
889 -
890 - wp_cache_set('table_builder__license_status', $status);
891 -
892 - return $status;
893 - }
894 -
895 - public static function is_local() {
896 - $valid_domains = [
897 - ".academy", ".accountant", ".accountants", ".actor", ".adult", ".africa", ".agency", ".airforce",
898 - ".apartments", ".app", ".army", ".art", ".asia", ".associates", ".attorney", ".auction", ".audio",
899 - ".auto", ".baby", ".band", ".bar", ".bargains", ".beer", ".berlin", ".best", ".bid", ".bike",
900 - ".bingo", ".bio", ".biz", ".black", ".blackfriday", ".blog", ".blue", ".boston", ".boutique",
901 - ".build", ".builders", ".business", ".buzz", ".cab", ".cafe", ".cam", ".camera", ".camp",
902 - ".capital", ".car", ".cards", ".care", ".careers", ".cars", ".casa", ".cash", ".casino",
903 - ".catering", ".center", ".ceo", ".chat", ".cheap", ".christmas", ".church", ".city", ".claims",
904 - ".cleaning", ".click", ".clinic", ".clothing", ".cloud", ".club", ".coach", ".codes", ".coffee",
905 - ".college", ".com", ".community", ".company", ".computer", ".condos", ".construction",
906 - ".consulting", ".contact", ".contractors", ".cooking", ".cool", ".country", ".coupons",
907 - ".courses", ".credit", ".creditcard", ".cricket", ".cruises", ".cymru", ".cyou", ".dance",
908 - ".date", ".dating", ".day", ".deals", ".degree", ".delivery", ".democrat", ".dental",
909 - ".dentist", ".desi", ".design", ".dev", ".diamonds", ".diet", ".digital", ".direct", ".directory",
910 - ".discount", ".doctor", ".dog", ".domains", ".download", ".earth", ".eco", ".education",
911 - ".email", ".energy", ".engineer", ".engineering", ".enterprises", ".equipment", ".estate",
912 - ".events", ".exchange", ".expert", ".exposed", ".express", ".fail", ".faith", ".family",
913 - ".fans", ".farm", ".fashion", ".feedback", ".film", ".finance", ".financial", ".fish",
914 - ".fishing", ".fit", ".fitness", ".flights", ".florist", ".flowers", ".football", ".forsale",
915 - ".foundation", ".fun", ".fund", ".furniture", ".futbol", ".fyi", ".gallery", ".game", ".games",
916 - ".garden", ".gay", ".gdn", ".gift", ".gifts", ".gives", ".glass", ".global", ".gmbh", ".gold",
917 - ".golf", ".graphics", ".gratis", ".green", ".gripe", ".group", ".guide", ".guitars", ".guru",
918 - ".hamburg", ".haus", ".health", ".healthcare", ".help", ".hiphop", ".hockey", ".holdings",
919 - ".holiday", ".horse", ".host", ".hosting", ".house", ".how", ".icu", ".immo", ".immobilien",
920 - ".inc", ".industries", ".info", ".ink", ".institute", ".insure", ".international", ".investments",
921 - ".irish", ".jetzt", ".jewelry", ".juegos", ".kaufen", ".kim", ".kitchen", ".kiwi", ".krd",
922 - ".kyoto", ".land", ".lat", ".lawyer", ".lease", ".legal", ".lgbt", ".life", ".lighting",
923 - ".limited", ".limo", ".link", ".live", ".llc", ".loan", ".loans", ".lol", ".london", ".love",
924 - ".ltd", ".ltda", ".luxury", ".maison", ".management", ".market", ".marketing", ".mba", ".media",
925 - ".melbourne", ".memorial", ".men", ".menu", ".miami", ".mobi", ".moda", ".moe", ".mom", ".money",
926 - ".monster", ".mortgage", ".movie", ".nagoya", ".name", ".navy", ".net", ".network", ".new",
927 - ".news", ".ninja", ".nyc", ".observer", ".okinawa", ".one", ".onl", ".online", ".org", ".osaka",
928 - ".page", ".paris", ".partners", ".parts", ".party", ".photo", ".photography", ".photos", ".pics",
929 - ".pictures", ".pink", ".pizza", ".place", ".plumbing", ".plus", ".poker", ".porn", ".press",
930 - ".pro", ".productions", ".properties", ".property", ".protection", ".pub", ".racing", ".realty",
931 - ".recipes", ".red", ".rehab", ".reise", ".reisen", ".rent", ".rentals", ".repair", ".report",
932 - ".republican", ".rest", ".restaurant", ".review", ".reviews", ".rip", ".rocks", ".rodeo",
933 - ".run", ".ryukyu", ".sale", ".sarl", ".school", ".schule", ".science", ".security", ".services",
934 - ".sex", ".sexy", ".shiksha", ".shoes", ".shop", ".shopping", ".show", ".singles", ".site",
935 - ".ski", ".soccer", ".social", ".software", ".solar", ".solutions", ".soy", ".space", ".storage",
936 - ".store", ".stream", ".studio", ".study", ".style", ".sucks", ".supplies", ".supply", ".support",
937 - ".surf", ".surgery", ".sydney", ".systems", ".tattoo", ".tax", ".taxi", ".team", ".tech",
938 - ".technology", ".tel", ".tennis", ".theater", ".theatre", ".tienda", ".tips", ".tires", ".today",
939 - ".tokyo", ".tools", ".top", ".tours", ".town", ".toys", ".trade", ".training", ".travel",
940 - ".tube", ".university", ".uno", ".vacations", ".vegas", ".ventures", ".vet", ".viajes", ".video",
941 - ".villas", ".vin", ".vip", ".vision", ".vodka", ".vote", ".voting", ".voto", ".voyage", ".wales",
942 - ".watch", ".webcam", ".website", ".wedding", ".wiki", ".win", ".wine", ".work", ".works",
943 - ".world", ".wtf", ".xn--3ds443g", ".xn--6frz82g", ".xxx", ".xyz", ".yoga", ".yokohama", ".zone"
944 - ];
945 -
946 - $host = !empty($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
947 -
948 - // get the domain
949 - $domain = explode('.', $host);
950 - if(count($domain) >= 2) {
951 - $domain = '.' . $domain[count($domain) - 1];
952 - } else {
953 - $domain = null;
954 - }
955 -
956 - return !in_array($domain, $valid_domains);
957 - }
958 -}
1 +<?php
2 +/**
3 + * General-purpose helper methods shared across the plugin
4 + *
5 + * @package TableKit
6 + */
7 +
8 +namespace TableBuilder\Helpers;
9 +
10 +defined( 'ABSPATH' ) || exit;
11 +
12 +/**
13 + * Global helper class.
14 + *
15 + * @since 1.0.0
16 + */
17 +class Utils {
18 +
19 + /**
20 + * Returns an array of allowed HTML tags and attributes for SVG elements.
21 + *
22 + * @return array The array of allowed HTML tags and attributes.
23 + */
24 + public static function svg_allowed_html() {
25 + $allowed_svg_tags = array(
26 + 'svg' => array(
27 + 'xmlns' => true,
28 + 'width' => true,
29 + 'height' => true,
30 + 'viewBox' => true,
31 + 'viewbox' => true,
32 + 'fill' => true,
33 + 'class' => true,
34 + 'aria-hidden' => true,
35 + 'aria-labelledby' => true,
36 + 'role' => true,
37 + 'preserveaspectratio' => true,
38 + 'version' => true,
39 + ),
40 + 'title' => array( 'title' => true ),
41 + 'g' => array(
42 + 'transform' => true,
43 + 'style' => true,
44 + 'id' => true,
45 + ),
46 + 'path' => array(
47 + 'd' => true,
48 + 'fill' => true,
49 + 'fill-rule' => true,
50 + 'transform' => true,
51 + 'style' => true,
52 + 'opacity' => true,
53 + 'stroke' => true,
54 + 'stroke-width' => true,
55 + 'stroke-miterlimit' => true,
56 + 'stroke-linecap' => true,
57 + 'stroke-linejoin' => true,
58 + 'fill-opacity' => true,
59 + ),
60 + 'circle' => array(
61 + 'cx' => true,
62 + 'cy' => true,
63 + 'r' => true,
64 + 'fill' => true,
65 + 'stroke' => true,
66 + 'stroke-width' => true,
67 + ),
68 + 'ellipse' => array(
69 + 'cx' => true,
70 + 'cy' => true,
71 + 'rx' => true,
72 + 'ry' => true,
73 + 'fill' => true,
74 + 'stroke' => true,
75 + 'stroke-width' => true,
76 + ),
77 + 'line' => array(
78 + 'x1' => true,
79 + 'y1' => true,
80 + 'x2' => true,
81 + 'y2' => true,
82 + 'stroke' => true,
83 + 'stroke-width' => true,
84 + ),
85 + 'polygon' => array(
86 + 'points' => true,
87 + 'fill' => true,
88 + 'stroke' => true,
89 + 'stroke-width' => true,
90 + ),
91 + 'polyline' => array(
92 + 'points' => true,
93 + 'fill' => true,
94 + 'stroke' => true,
95 + 'stroke-width' => true,
96 + ),
97 + 'rect' => array(
98 + 'x' => true,
99 + 'y' => true,
100 + 'width' => true,
101 + 'height' => true,
102 + 'fill' => true,
103 + 'stroke' => true,
104 + 'stroke-width' => true,
105 + ),
106 + 'text' => array(
107 + 'x' => true,
108 + 'y' => true,
109 + 'dx' => true,
110 + 'dy' => true,
111 + 'text-anchor' => true,
112 + 'style' => true,
113 + ),
114 + 'tspan' => array(
115 + 'x' => true,
116 + 'y' => true,
117 + 'dx' => true,
118 + 'dy' => true,
119 + 'text-anchor' => true,
120 + 'style' => true,
121 + ),
122 + 'defs' => array(),
123 + 'lineargradient' => array(
124 + 'id' => true,
125 + 'x1' => true,
126 + 'y1' => true,
127 + 'x2' => true,
128 + 'y2' => true,
129 + 'gradientunits' => true,
130 + ),
131 + 'stop' => array(
132 + 'offset' => true,
133 + 'style' => true,
134 + 'stop-color' => true,
135 + 'stop-opacity' => true,
136 + ),
137 + 'radialgradient' => array(
138 + 'id' => true,
139 + 'cx' => true,
140 + 'cy' => true,
141 + 'r' => true,
142 + 'gradientunits' => true,
143 + 'gradienttransform' => true,
144 + ),
145 + );
146 +
147 + /**
148 + * Filters the allowed SVG tags and attributes used when sanitizing block output.
149 + *
150 + * @since Unknown
151 + *
152 + * @param array $allowed_svg_tags Allowed SVG elements and their attributes, keyed by tag name.
153 + */
154 + return apply_filters( 'table_builder_allowed_svg_attrs_tags', $allowed_svg_tags );
155 + }
156 +
157 + /**
158 + * Returns an array of allowed JSON attribute tags.
159 + *
160 + * This function defines an array of allowed JSON attribute tags and their corresponding properties.
161 + * The array includes tags such as 'object', 'array', 'string', 'number', 'integer', 'boolean', 'null',
162 + * 'enum', 'const', 'oneOf', 'allOf', 'anyOf', 'not', 'if', 'then', 'else', and 'format'.
163 + *
164 + * @return array The array of allowed JSON attribute tags.
165 + */
166 + public static function allowed_json_attrs_tags() {
167 + $allowed_json_tags = array(
168 + 'object' => array(
169 + 'type' => true,
170 + 'properties' => true,
171 + 'required' => true,
172 + 'additionalProperties' => true,
173 + 'propertyNames' => true,
174 + 'dependencies' => true,
175 + 'minProperties' => true,
176 + 'maxProperties' => true,
177 + ),
178 + 'array' => array(
179 + 'type' => true,
180 + 'items' => true,
181 + 'minItems' => true,
182 + 'maxItems' => true,
183 + 'uniqueItems' => true,
184 + ),
185 + 'string' => array(
186 + 'type' => true,
187 + 'minLength' => true,
188 + 'maxLength' => true,
189 + 'pattern' => true,
190 + 'format' => true,
191 + 'contentEncoding' => true,
192 + 'contentMediaType' => true,
193 + ),
194 + 'number' => array(
195 + 'type' => true,
196 + 'minimum' => true,
197 + 'maximum' => true,
198 + 'exclusiveMinimum' => true,
199 + 'exclusiveMaximum' => true,
200 + 'multipleOf' => true,
201 + ),
202 + 'integer' => array(
203 + 'type' => true,
204 + 'minimum' => true,
205 + 'maximum' => true,
206 + 'exclusiveMinimum' => true,
207 + 'exclusiveMaximum' => true,
208 + ),
209 + 'boolean' => array(
210 + 'type' => true,
211 + ),
212 + 'null' => array(
213 + 'type' => true,
214 + ),
215 + 'enum' => array(
216 + 'type' => true,
217 + 'enum' => true,
218 + ),
219 + 'const' => array(
220 + 'type' => true,
221 + 'const' => true,
222 + ),
223 + 'oneOf' => array(
224 + 'type' => true,
225 + 'oneOf' => true,
226 + ),
227 + 'allOf' => array(
228 + 'type' => true,
229 + 'allOf' => true,
230 + ),
231 + 'anyOf' => array(
232 + 'type' => true,
233 + 'anyOf' => true,
234 + ),
235 + 'not' => array(
236 + 'type' => true,
237 + 'not' => true,
238 + ),
239 + 'if' => array(
240 + 'type' => true,
241 + 'if' => true,
242 + ),
243 + 'then' => array(
244 + 'type' => true,
245 + 'then' => true,
246 + ),
247 + 'else' => array(
248 + 'type' => true,
249 + 'else' => true,
250 + ),
251 + 'format' => array(
252 + 'type' => true,
253 + 'format' => true,
254 + ),
255 + 'title' => true,
256 + 'description' => true,
257 + 'default' => true,
258 + 'examples' => true,
259 + '$ref' => true,
260 + '$id' => true,
261 + '$schema' => true,
262 + // Adding Lottie-specific keys.
263 + 'v' => true,
264 + 'meta' => true,
265 + 'fr' => true,
266 + 'ip' => true,
267 + 'op' => true,
268 + 'w' => true,
269 + 'h' => true,
270 + 'nm' => true,
271 + 'ddd' => true,
272 + 'assets' => true,
273 + 'layers' => true,
274 + 'markers' => true,
275 + );
276 +
277 + /**
278 + * Filters the allowed JSON Schema/Lottie attribute keys used when sanitizing block output.
279 + *
280 + * @since Unknown
281 + *
282 + * @param array $allowed_json_tags Allowed JSON keys, keyed by key name.
283 + */
284 + return apply_filters( 'table_builder_allowed_json_attrs_tags', $allowed_json_tags );
285 + }
286 +
287 + /**
288 + * Returns the allowed HTML tags and attributes for the iframe element.
289 + *
290 + * @return array The allowed HTML tags and attributes.
291 + */
292 + public static function iframe_allowed_html() {
293 + return array(
294 + 'iframe' => array(
295 + 'src' => true,
296 + 'name' => true,
297 + 'sandbox' => true,
298 + 'width' => true,
299 + 'height' => true,
300 + 'marginheight' => true,
301 + 'marginwidth' => true,
302 + 'scrolling' => true,
303 + 'allowfullscreen' => true,
304 + 'frameborder' => true,
305 + 'title' => true,
306 + 'id' => true,
307 + 'class' => true,
308 + 'style' => true,
309 + 'tabindex' => true,
310 + 'allow' => true,
311 + ),
312 + );
313 + }
314 +
315 + /**
316 + * Returns the allowed HTML tags and attributes for the "gdc" (GenerateBlocks
317 + * dynamic content) shortcode-style tag used in migrated content.
318 + *
319 + * @return array The allowed HTML tags and attributes.
320 + */
321 + public static function gdc_allowed_html() {
322 + return array(
323 + 'gdc' => array(
324 + 'selectedpath' => true,
325 + 'class' => true,
326 + 'id' => true,
327 + 'fallback' => true,
328 + 'postcustomfield' => true,
329 + 'postcustomfieldkey' => true,
330 + 'postdatetype' => true,
331 + 'dateformat' => true,
332 + 'customdateformat' => true,
333 + 'excerptlength' => true,
334 + 'tagindex' => true,
335 + 'timetype' => true,
336 + 'timeformat' => true,
337 + 'customtimeformat' => true,
338 + 'categoryindex' => true,
339 + 'nocomment' => true,
340 + 'singlecomment' => true,
341 + 'multicomments' => true,
342 + 'currentdateformat' => true,
343 + 'customcurrentdateformat' => true,
344 + 'currenttimeformat' => true,
345 + 'customcurrenttimeformat' => true,
346 + 'authorinfo' => true,
347 + 'currentuserinfo' => true,
348 + 'acfgroup' => true,
349 + 'acffield' => true,
350 + ),
351 + );
352 + }
353 +
354 + /**
355 + * Returns the allowed HTML tags and attributes for the img element.
356 + *
357 + * @return array The allowed HTML tags and attributes.
358 + */
359 + public static function img_allowed_html() {
360 + return array(
361 + 'img' => array(
362 + 'alt' => true,
363 + 'src' => true,
364 + 'srcset' => true,
365 + 'class' => true,
366 + 'height' => true,
367 + 'width' => true,
368 + ),
369 + );
370 + }
371 +
372 + /**
373 + * Returns the allowed HTML tags and attributes for the style element.
374 + *
375 + * @return array The allowed HTML tags and attributes.
376 + */
377 + public static function style_allowed_html() {
378 + return array(
379 + 'style' => array(
380 + 'class' => true,
381 + 'id' => true,
382 + ),
383 + );
384 + }
385 +
386 + /**
387 + * Returns the default responsive breakpoint list (Desktop/Tablet/Mobile)
388 + * used across the block editor's responsive controls.
389 + *
390 + * @return array[] List of device descriptors (label/slug/value/direction/isActive/isRequired).
391 + */
392 + public static function get_device_list() {
393 + $default_device_list = array(
394 + array(
395 + 'label' => 'Desktop',
396 + 'slug' => 'Desktop',
397 + 'value' => 'base',
398 + 'direction' => 'max',
399 + 'isActive' => true,
400 + 'isRequired' => true,
401 + ),
402 + array(
403 + 'label' => 'Tablet',
404 + 'slug' => 'Tablet',
405 + 'value' => '1024',
406 + 'direction' => 'max',
407 + 'isActive' => true,
408 + 'isRequired' => true,
409 + ),
410 + array(
411 + 'label' => 'Mobile',
412 + 'slug' => 'Mobile',
413 + 'value' => '767',
414 + 'direction' => 'max',
415 + 'isActive' => true,
416 + 'isRequired' => true,
417 + ),
418 + );
419 +
420 + return $default_device_list;
421 + }
422 +
423 + /**
424 + * Injects a `class="table-builder-icon"` attribute into a raw `<svg ...>` string.
425 + *
426 + * @since 1.0.0
427 + *
428 + * @param string $svg Raw SVG markup.
429 + * @return string The SVG markup with the class attribute injected, or unchanged if no `<svg` tag is found.
430 + */
431 + public static function add_class_to_svg( $svg ) {
432 + $original_string = $svg;
433 + $substring_to_add = "class='table-builder-icon' ";
434 +
435 + $position = strpos( $original_string, '<svg' );
436 +
437 + if ( false !== $position ) {
438 + $svg = substr_replace( $original_string, $substring_to_add, $position + 5, 0 );
439 + return $svg;
440 + }
441 +
442 + return $svg;
443 + }
444 +
445 + /**
446 + * Retrieves the dynamic block wrapper attributes.
447 + *
448 + * This function retrieves the wrapper attributes for a dynamic block.
449 + * It checks if the function "get_block_wrapper_attributes" exists and if the block is not empty.
450 + * If both conditions are met, it retrieves the block attributes, including the block ID.
451 + * It then merges the required attributes with any extra attributes provided.
452 + * Finally, it applies the "tablebuilder/dynamic_block_wrapper_attributes" filter and returns the wrapper attributes.
453 + *
454 + * @param object $block The dynamic block object.
455 + * @param array $extra_attrs Additional attributes to be merged with the required attributes.
456 + * @return string The wrapper attributes for the dynamic block.
457 + */
458 + public static function get_dynamic_block_wrapper_attributes( $block, $extra_attrs = array() ) {
459 + if ( function_exists( 'get_block_wrapper_attributes' ) && ! empty( $block ) ) {
460 + $block_attrs = $block->attributes;
461 + $block_id = $block_attrs['blockID'];
462 + $required_attrs = array(
463 + 'id' => 'block-' . $block_id,
464 + );
465 + /**
466 + * Filters a dynamic block's wrapper attributes before they're rendered.
467 + *
468 + * @since Unknown
469 + *
470 + * @param array $wrapper_attrs Merged required + extra wrapper attributes (e.g. "id").
471 + * @param object $block The dynamic block object.
472 + */
473 + // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores -- slash-namespaced hook name is this plugin's established public API (used by the Pro add-on); renaming would be a breaking change.
474 + $wrapper_attrs = apply_filters( 'tablebuilder/dynamic_block_wrapper_attributes', array_merge( $required_attrs, $extra_attrs ), $block );
475 + return get_block_wrapper_attributes( $wrapper_attrs );
476 + }
477 + }
478 +
479 + /**
480 + * Extends the allowed HTML tags for post content.
481 + *
482 + * @return array The extended array of allowed HTML tags.
483 + */
484 + public static function post_kses_extend_allowed_html() {
485 + $default_post_allowed_html = wp_kses_allowed_html( 'post' );
486 +
487 + $post_allowed_html = array_merge( $default_post_allowed_html, self::svg_allowed_html(), self::iframe_allowed_html(), self::gdc_allowed_html(), self::style_allowed_html() );
488 +
489 + return $post_allowed_html;
490 + }
491 +
492 + /**
493 + * Retrieves the plugin's settings, or a specific key/field/inner-field within them,
494 + * from the "table_builder_settings_list" option.
495 + *
496 + * @param string $key Optional. Top-level settings key to look up.
497 + * @param string $field Optional. Field within that key to look up; defaults to "status"
498 + * (returned as a bool, true when its value is "active").
499 + * @param string $inner_field Optional. Nested field within $field to look up.
500 + * @return mixed The retrieved settings value, or false if not found.
501 + */
502 + public static function get_settings( $key = '', $field = 'status', $inner_field = '' ) {
503 + $settings = get_option( 'table_builder_settings_list' );
504 +
505 + // check if $key & $field both empty.
506 + if ( empty( $key ) && empty( $field ) ) {
507 + return $settings;
508 + }
509 +
510 + // check for primary key.
511 + if ( ! empty( $key ) && ! empty( $settings[ $key ] ) ) {
512 + $settings = $settings[ $key ];
513 + }
514 +
515 + // check for primary field.
516 + if ( ! empty( $field ) ) {
517 + if ( 'status' === $field && ! empty( $settings[ $field ] ) ) {
518 + return ( 'active' === $settings[ $field ] ) ? true : false;
519 + } else {
520 + $settings = ! empty( $settings[ $field ] ) ? $settings[ $field ] : false;
521 + }
522 + }
523 +
524 + // check for inner field.
525 + if ( ! empty( $inner_field ) ) {
526 + if ( isset( $settings[ $inner_field ]['value'] ) ) {
527 + $settings = $settings[ $inner_field ]['value'];
528 + } else {
529 + $settings = ! empty( $settings[ $inner_field ] ) ? $settings[ $inner_field ] : false;
530 + }
531 + }
532 +
533 + return $settings;
534 + }
535 +
536 + /**
537 + * Returns an array representing the border value based on the given key.
538 + *
539 + * @param mixed $key The key used to determine the border value.
540 + * @return array An array representing the border value. The array contains the following keys:
541 + * - 'border': The border value, or null if the key is null.
542 + */
543 + public static function get_border_value( $key ) {
544 +
545 + if ( ! is_array( $key ) ) {
546 + return array( 'border' => null );
547 + }
548 +
549 + // Per-side border box: keys are direction names (top/right/bottom/left),
550 + // each holding its own {width, style, color}.
551 + $sides = array( 'top', 'right', 'bottom', 'left' );
552 + $is_per_side = count( array_intersect( array_keys( $key ), $sides ) ) > 0;
553 +
554 + if ( $is_per_side ) {
555 + $border = array();
556 + foreach ( $key as $direction => $value ) {
557 + if ( ! is_array( $value ) ) {
558 + continue;
559 + }
560 + if ( isset( $value['width'] ) && '' !== $value['width'] ) {
561 + $color = ! empty( $value['color'] ) ? $value['color'] : 'currentColor';
562 + $style = ! empty( $value['style'] ) ? $value['style'] : 'solid';
563 + $border[ "border-{$direction}" ] = "{$value['width']} {$style} {$color}";
564 + }
565 + }
566 + return $border;
567 + }
568 +
569 + // Flat border: a defined width alone is enough to emit a declaration —
570 + // this ensures an explicit 0 always produces `border-width: 0` instead
571 + // of being silently dropped when style/color weren't also set (which
572 + // let a theme's/Elementor's default table border show through).
573 + if ( isset( $key['width'] ) && '' !== $key['width'] ) {
574 + $color = ! empty( $key['color'] ) ? $key['color'] : 'currentColor';
575 + $style = ! empty( $key['style'] ) ? $key['style'] : 'solid';
576 + return array( 'border' => "{$key['width']} {$style} {$color}" );
577 + }
578 +
579 + if ( ! empty( $key['color'] ) ) {
580 + $style = ! empty( $key['style'] ) ? $key['style'] : 'solid';
581 + return array( 'border' => "1px {$style} {$key['color']}" );
582 + }
583 +
584 + return array( 'border' => null );
585 + }
586 +
587 + /**
588 + * Builds a CSS box-model value (margin/padding/border-radius style) from a
589 + * {top,right,bottom,left} array, collapsing to a shorthand string when all
590 + * four sides share values.
591 + *
592 + * @param array $key Box values keyed by 'top'/'right'/'bottom'/'left'.
593 + * @param string $property CSS property prefix (e.g. "margin", "padding"), or
594 + * "border-radius" for the corner-radius longhand variant.
595 + * @return array CSS declarations keyed by property name (shorthand or per-side longhand).
596 + */
597 + public static function get_box_value( $key = array(), $property = '' ) {
598 + $top = isset( $key['top'] ) ? $key['top'] : null;
599 + $right = isset( $key['right'] ) ? $key['right'] : null;
600 + $bottom = isset( $key['bottom'] ) ? $key['bottom'] : null;
601 + $left = isset( $key['left'] ) ? $key['left'] : null;
602 +
603 + $box_object = array(
604 + 'top' => $top,
605 + 'right' => $right,
606 + 'bottom' => $bottom,
607 + 'left' => $left,
608 + );
609 + $count = count(
610 + array_filter(
611 + $box_object,
612 + function ( $value ) {
613 + return null !== $value;
614 + }
615 + )
616 + );
617 +
618 + if ( 0 === $count ) {
619 + return array( $property => null );
620 + }
621 +
622 + if ( 4 === $count ) {
623 + $box_values = '';
624 +
625 + if ( $top === $bottom && $top === $right && $top === $left ) {
626 + $box_values = $top;
627 + } elseif ( $top === $bottom && $left === $right ) {
628 + $box_values = "{$top} {$right}";
629 + } else {
630 + $box_values = "{$top} {$right} {$bottom} {$left}";
631 + }
632 +
633 + return array( $property => $box_values );
634 + }
635 +
636 + $final_box = array();
637 +
638 + if ( 'border-radius' !== $property ) {
639 + foreach ( $box_object as $direction => $value ) {
640 + $final_box[ "{$property}-{$direction}" ] = isset( $key[ $direction ] ) ? $key[ $direction ] : null;
641 + }
642 +
643 + return $final_box;
644 + }
645 +
646 + if ( 'border-radius' === $property ) {
647 + $final_box['border-top-left-radius'] = $top;
648 + $final_box['border-top-right-radius'] = $right;
649 + $final_box['border-bottom-right-radius'] = $bottom;
650 + $final_box['border-bottom-left-radius'] = $left;
651 +
652 + return $final_box;
653 + }
654 + }
655 +
656 + /**
657 + * Retrieves the color based on the given type and color.
658 + *
659 + * @param string $type The type of color to retrieve. Can be either 'gradient' or 'color'.
660 + * @param string $color The color value.
661 + * @return string The retrieved color.
662 + */
663 + public static function get_color( $type, $color ) {
664 +
665 + if ( empty( $color ) || 0 === strpos( $color, 'linear-gradient(' ) || 0 === strpos( $color, 'radial-gradient(' ) || 0 === strpos( $color, '#' ) ) {
666 + return $color;
667 + }
668 +
669 + $color_parts = explode( ',', $color, 2 );
670 +
671 + $color_parts1 = $color_parts[0];
672 + $color_parts2 = $color_parts[1];
673 +
674 + $bg_color = '';
675 +
676 + if ( 'gradient' === $type ) {
677 + $bg_color = 'var(--wp--preset--gradient--' . $color_parts1 . ',' . $color_parts2 . ')';
678 +
679 + } else {
680 + $bg_color = 'var(--wp--preset--color--' . $color_parts1 . ',' . $color_parts2 . ')';
681 + }
682 + return $bg_color;
683 + }
684 +
685 + /**
686 + * Builds CSS background-* declarations (color, gradient, or image with
687 + * position/size/repeat/attachment) from a block's background attribute object.
688 + *
689 + * @param array $background Background attribute object (backgroundType plus the
690 + * relevant color/gradient/image sub-fields).
691 + * @param string $device Responsive device key used for position/size sub-fields; default "Desktop".
692 + * @return array CSS declarations keyed by "background-*" property name.
693 + */
694 + public static function fill_background_generator( $background, $device = 'Desktop' ) {
695 +
696 + $fill_background = array(
697 + 'background-image' => '',
698 + );
699 +
700 + if ( isset( $background['backgroundType'] ) && 'classic' === $background['backgroundType'] ) {
701 + $fill_background['background-color'] = isset( $background['backgroundColor'] ) ? self::get_color( 'color', $background['backgroundColor'] ) : '';
702 + }
703 +
704 + if ( isset( $background['backgroundType'] ) && 'gradient' === $background['backgroundType'] && ! empty( $background['gradient'] ) ) {
705 +
706 + $fill_background['background-image'] = isset( $background['gradient'] ) ? ( self::get_color( 'gradient', $background['gradient'] ) ?? '' ) : '';
707 + }
708 +
709 + if ( isset( $background['backgroundType'] ) && 'image' === $background['backgroundType'] && ! empty( $background['backgroundImage'] ) ) {
710 + if ( ! empty( $background['backgroundImage']['imageUrl'] ) ) {
711 + $fill_background['background-image'] = "url({$background['backgroundImage']['imageUrl']})";
712 + }
713 +
714 + if ( ! empty( $background['backgroundAttachment'] ) ) {
715 + $fill_background['background-attachment'] = $background['backgroundAttachment'];
716 + }
717 +
718 + if ( ! empty( $background['backgroundPosition'][ $device ] ) && 'custom' !== $background['backgroundPosition'][ $device ] ) {
719 + $fill_background['background-position'] = $background['backgroundPosition'][ $device ];
720 + }
721 +
722 + if (
723 + ! empty( $background['backgroundPosition'][ $device ] ) && 'custom' === $background['backgroundPosition'][ $device ] &&
724 + ! empty( $background['customPositionX'][ $device ] ) && ! empty( $background['customPositionY'][ $device ] )
725 + ) {
726 + $fill_background['background-position'] = self::get_slider_value( $background['customPositionX'][ $device ] ) . ' ' . self::get_slider_value( $background['customPositionY'][ $device ] );
727 + }
728 +
729 + if ( ! empty( $background['backgroundSize'] ) && 'custom' !== $background['backgroundSize'] ) {
730 + $fill_background['background-size'] = $background['backgroundSize'];
731 + }
732 +
733 + if (
734 + ! empty( $background['backgroundSize'] ) && 'custom' === $background['backgroundSize'] &&
735 + ! empty( $background['customSize'][ $device ] )
736 + ) {
737 + $fill_background['background-size'] = self::get_slider_value( $background['customSize'][ $device ] ) . ' auto';
738 + }
739 +
740 + if ( ! empty( $background['backgroundRepeat'] ) ) {
741 + $fill_background['background-repeat'] = $background['backgroundRepeat'];
742 + }
743 + }
744 +
745 + return $fill_background;
746 + }
747 +
748 + /**
749 + * Builds CSS font-, text-, line-height, letter-spacing, and word-spacing
750 + * declarations from a typography attribute object for the given responsive device.
751 + *
752 + * @param array $key Typography attribute object (fontFamily/fontSize/fontStyle/
753 + * fontWeight/textDecoration/textTransform/lineHeight/letterSpacing/wordSpacing).
754 + * @param string $device Responsive device key (e.g. "Desktop"); non-Desktop devices
755 + * omit font-family/style/weight/text-decoration/text-transform,
756 + * which are treated as desktop-only.
757 + * @return array CSS declarations keyed by property name.
758 + */
759 + public static function get_typography_value( $key, $device ) {
760 + if ( 'Desktop' === $device ) {
761 + return array(
762 + 'font-family' => isset( $key['fontFamily']['value'] ) ? $key['fontFamily']['value'] : null,
763 + 'font-size' => isset( $key['fontSize'][ $device ]['size'] ) && isset( $key['fontSize'][ $device ]['unit'] )
764 + ? $key['fontSize'][ $device ]['size'] . $key['fontSize'][ $device ]['unit']
765 + : null,
766 + 'font-style' => isset( $key['fontStyle'] ) ? $key['fontStyle'] : null,
767 + 'font-weight' => isset( $key['fontWeight']['value'] ) ? $key['fontWeight']['value'] : null,
768 + 'text-decoration' => isset( $key['textDecoration'] ) ? $key['textDecoration'] : null,
769 + 'text-transform' => isset( $key['textTransform'] ) ? $key['textTransform'] : null,
770 + 'line-height' => isset( $key['lineHeight'][ $device ]['size'] ) && isset( $key['lineHeight'][ $device ]['unit'] )
771 + ? $key['lineHeight'][ $device ]['size'] . $key['lineHeight'][ $device ]['unit']
772 + : null,
773 + 'letter-spacing' => isset( $key['letterSpacing'][ $device ]['size'] ) && isset( $key['letterSpacing'][ $device ]['unit'] )
774 + ? $key['letterSpacing'][ $device ]['size'] . $key['letterSpacing'][ $device ]['unit']
775 + : null,
776 + 'word-spacing' => isset( $key['wordSpacing'][ $device ]['size'] ) && isset( $key['wordSpacing'][ $device ]['unit'] )
777 + ? $key['wordSpacing'][ $device ]['size'] . $key['wordSpacing'][ $device ]['unit']
778 + : null,
779 + );
780 + } else {
781 + return array(
782 + 'font-size' => isset( $key['fontSize'][ $device ]['size'] ) && isset( $key['fontSize'][ $device ]['unit'] )
783 + ? $key['fontSize'][ $device ]['size'] . $key['fontSize'][ $device ]['unit']
784 + : null,
785 + 'line-height' => isset( $key['lineHeight'][ $device ]['size'] ) && isset( $key['lineHeight'][ $device ]['unit'] )
786 + ? $key['lineHeight'][ $device ]['size'] . $key['lineHeight'][ $device ]['unit']
787 + : null,
788 + 'letter-spacing' => isset( $key['letterSpacing'][ $device ]['size'] ) && isset( $key['letterSpacing'][ $device ]['unit'] )
789 + ? $key['letterSpacing'][ $device ]['size'] . $key['letterSpacing'][ $device ]['unit']
790 + : null,
791 + 'word-spacing' => isset( $key['wordSpacing'][ $device ]['size'] ) && isset( $key['wordSpacing'][ $device ]['unit'] )
792 + ? $key['wordSpacing'][ $device ]['size'] . $key['wordSpacing'][ $device ]['unit']
793 + : null,
794 + );
795 + }
796 + }
797 +
798 + /**
799 + * Gets slider value.
800 + *
801 + * Similar to getSliderValue function in JS helper.
802 + *
803 + * @param array $key Slider value array with "size" and "unit" keys.
804 + * @return string|null The combined "{size}{unit}" string, or null if no size is set.
805 + */
806 + public static function get_slider_value( $key ) {
807 + $value = '';
808 +
809 + if ( ! empty( $key['size'] ) && ! empty( $key['unit'] ) ) {
810 + $value = $key['size'] . $key['unit'];
811 + } elseif ( ! empty( $key['size'] ) && empty( $key['unit'] ) ) {
812 + $value = $key['size'];
813 + } else {
814 + $value = null;
815 + }
816 +
817 + return $value;
818 + }
819 +
820 + /**
821 + * Converts a per-device map of {selector, ...cssValues} style records into
822 + * per-device CSS text blocks.
823 + *
824 + * @param array $raw_css Style records keyed by device slug (desktop/tablet/mobile/etc.),
825 + * each an array of {selector, ...property=>value} entries.
826 + * @return array<string,string> Compiled CSS text keyed by device slug.
827 + */
828 + public static function parse_css( $raw_css ) {
829 +
830 + $styles = array();
831 + $device_list = array( 'desktop', 'tablet', 'mobile', 'tabletlandscape', 'mobilelandscape', 'laptop', 'widescreen' );
832 +
833 + foreach ( $device_list as $device ) {
834 + $device_styles = $raw_css[ $device ] ?? array();
835 +
836 + $styles[ $device ] = array_map(
837 + function ( $style ) {
838 + if ( ! is_array( $style ) || ! isset( $style['selector'] ) ) {
839 + return '';
840 + }
841 +
842 + $selector = $style['selector'];
843 + $css_values = array_filter(
844 + $style,
845 + function ( $value, $key ) {
846 + return 'selector' !== $key && null !== $value && '' !== $value && ! is_numeric( $value ) && ! in_array( $value, array( 'px', 'em', 'rem', '%', 'vh', 'vw' ), true ) && false === strpos( $value, 'undefined' );
847 + },
848 + ARRAY_FILTER_USE_BOTH
849 + );
850 +
851 + if ( empty( $css_values ) ) {
852 + return '';
853 + }
854 +
855 + return "{$selector} { " . implode(
856 + ' ',
857 + array_map(
858 + function ( $key, $value ) {
859 + return "{$key}: {$value};";
860 + },
861 + array_keys( $css_values ),
862 + $css_values
863 + )
864 + ) . ' }';
865 + },
866 + $device_styles
867 + );
868 + }
869 +
870 + $device_styles = array_map(
871 + function ( $style ) {
872 + return implode( "\n", $style );
873 + },
874 + $styles
875 + );
876 +
877 + return $device_styles;
878 + }
879 +
880 + /**
881 + * Checks whether a parsed block is one of this plugin's "tablebuilder/*" blocks,
882 + * optionally requiring a specific (possibly nested) attribute to be present.
883 + *
884 + * @param string $block_content Rendered block HTML content.
885 + * @param array $parsed_block Parsed block array (as passed to a `render_block` callback).
886 + * @param string $attrs Optional. Top-level attribute key to require.
887 + * @param string $attrs2 Optional. Nested key within $attrs to require.
888 + * @return bool True if this is a table-builder block matching the given criteria.
889 + */
890 + public static function is_table_builder_block( $block_content, $parsed_block, $attrs = '', $attrs2 = '' ) {
891 + // phpcs:ignore Squiz.PHP.CommentedOutCode.Found -- explanatory prose comments, not commented-out code.
892 + // Check if $block_content is not empty.
893 + $has_block_content = ! empty( $block_content );
894 + // Check if $block['blockName'] is not empty and contains 'roxSlider'.
895 + $has_valid_block_name = isset( $parsed_block['blockName'] ) && false !== strpos( $parsed_block['blockName'], 'tablebuilder' );
896 +
897 + // Check if $block['attrs']['blockClass'] is not empty.
898 + $has_block_class = ! empty( $attrs ) && ! empty( $attrs2 )
899 + ? ! empty( $parsed_block['attrs'][ $attrs ][ $attrs2 ] )
900 + : ! empty( $parsed_block['attrs'][ $attrs ] ?? '' );
901 +
902 + if ( empty( $attrs ) && empty( $attrs2 ) ) {
903 + $has_block_class = true;
904 + }
905 +
906 + // Return true if all conditions are met.
907 + return $has_block_content && $has_valid_block_name && $has_block_class;
908 + }
909 +
910 + /**
911 + * Retrieves the link attributes based on the provided attribute array.
912 + *
913 + * @param array $attribute The attribute array containing the link data.
914 + * @return string The generated link attributes as a string.
915 + */
916 + public static function get_link_attributes( $attribute ) {
917 + if ( empty( $attribute['url'] ) ) {
918 + return '';
919 + }
920 +
921 + $link_data = array();
922 +
923 + $link_data['href'] = esc_url( $attribute['url'], wp_allowed_protocols() );
924 +
925 + ( isset( $attribute['newTab'] ) && $attribute['newTab'] ) ? $link_data['target'] = '_blank' : '';
926 +
927 + ( isset( $attribute['noFollow'] ) && $attribute['noFollow'] ) ? $link_data['rel'] = 'nofollow' : '';
928 +
929 + if ( isset( $attribute['customAttributes'] ) && is_array( $attribute['customAttributes'] ) ) {
930 + foreach ( $attribute['customAttributes'] as $key => $value ) {
931 + if ( ! empty( $value ) ) {
932 + $attr_key_value = explode( '|', $value );
933 +
934 + $attr_key = mb_strtolower( $attr_key_value[0] );
935 +
936 + // Not allowed characters are removed.
937 + preg_match( '/[-_a-z0-9]+/', $attr_key, $attr_key_matches );
938 +
939 + if ( empty( $attr_key_matches[0] ) ) {
940 + continue;
941 + }
942 +
943 + $attr_key = $attr_key_matches[0];
944 +
945 + // Javascript events and unescaped href are avoided.
946 + if ( 'href' === $attr_key || 'on' === substr( $attr_key, 0, 2 ) ) {
947 + continue;
948 + }
949 +
950 + if ( isset( $attr_key_value[1] ) ) {
951 + $attr_value = trim( $attr_key_value[1] );
952 + } else {
953 + $attr_value = '';
954 + }
955 +
956 + $link_data[ $attr_key ] = $attr_value;
957 + }
958 + }
959 + }
960 +
961 + $link_attributes = '';
962 + foreach ( $link_data as $key => $value ) {
963 + $link_attributes .= sprintf( '%s="%s" ', $key, esc_attr( $value ) );
964 + }
965 +
966 + return $link_attributes;
967 + }
968 +
969 + /**
970 + * Gets the plugin's cached license status ("valid" or "invalid"), based on
971 + * whether both the stored license key and license identifier are set.
972 + *
973 + * @return string "valid" or "invalid".
974 + */
975 + public static function status() {
976 +
977 + $cached = wp_cache_get( 'table_builder__license_status' );
978 +
979 + if ( false !== $cached ) {
980 + return $cached;
981 + }
982 +
983 + $oppai = get_option( '__table_builder_oppai__', '' );
984 + $key = get_option( '__table_builder_license_key__', '' );
985 + $status = 'invalid';
986 +
987 + if ( '' !== $oppai && '' !== $key ) {
988 + $status = 'valid';
989 + }
990 +
991 + wp_cache_set( 'table_builder__license_status', $status );
992 +
993 + return $status;
994 + }
995 +
996 + /**
997 + * Checks whether the current request's host's TLD is outside a known list of
998 + * public/commercial TLDs — used as a rough heuristic to detect local/dev
999 + * environments (e.g. `.test`, `.local`, `.localhost`) for license checks.
1000 + *
1001 + * @return bool True if the current host's TLD is NOT in the known public-TLD list
1002 + * (i.e. it looks like a local/dev environment).
1003 + */
1004 + public static function is_local() {
1005 + $valid_domains = array(
1006 + '.academy',
1007 + '.accountant',
1008 + '.accountants',
1009 + '.actor',
1010 + '.adult',
1011 + '.africa',
1012 + '.agency',
1013 + '.airforce',
1014 + '.apartments',
1015 + '.app',
1016 + '.army',
1017 + '.art',
1018 + '.asia',
1019 + '.associates',
1020 + '.attorney',
1021 + '.auction',
1022 + '.audio',
1023 + '.auto',
1024 + '.baby',
1025 + '.band',
1026 + '.bar',
1027 + '.bargains',
1028 + '.beer',
1029 + '.berlin',
1030 + '.best',
1031 + '.bid',
1032 + '.bike',
1033 + '.bingo',
1034 + '.bio',
1035 + '.biz',
1036 + '.black',
1037 + '.blackfriday',
1038 + '.blog',
1039 + '.blue',
1040 + '.boston',
1041 + '.boutique',
1042 + '.build',
1043 + '.builders',
1044 + '.business',
1045 + '.buzz',
1046 + '.cab',
1047 + '.cafe',
1048 + '.cam',
1049 + '.camera',
1050 + '.camp',
1051 + '.capital',
1052 + '.car',
1053 + '.cards',
1054 + '.care',
1055 + '.careers',
1056 + '.cars',
1057 + '.casa',
1058 + '.cash',
1059 + '.casino',
1060 + '.catering',
1061 + '.center',
1062 + '.ceo',
1063 + '.chat',
1064 + '.cheap',
1065 + '.christmas',
1066 + '.church',
1067 + '.city',
1068 + '.claims',
1069 + '.cleaning',
1070 + '.click',
1071 + '.clinic',
1072 + '.clothing',
1073 + '.cloud',
1074 + '.club',
1075 + '.coach',
1076 + '.codes',
1077 + '.coffee',
1078 + '.college',
1079 + '.com',
1080 + '.community',
1081 + '.company',
1082 + '.computer',
1083 + '.condos',
1084 + '.construction',
1085 + '.consulting',
1086 + '.contact',
1087 + '.contractors',
1088 + '.cooking',
1089 + '.cool',
1090 + '.country',
1091 + '.coupons',
1092 + '.courses',
1093 + '.credit',
1094 + '.creditcard',
1095 + '.cricket',
1096 + '.cruises',
1097 + '.cymru',
1098 + '.cyou',
1099 + '.dance',
1100 + '.date',
1101 + '.dating',
1102 + '.day',
1103 + '.deals',
1104 + '.degree',
1105 + '.delivery',
1106 + '.democrat',
1107 + '.dental',
1108 + '.dentist',
1109 + '.desi',
1110 + '.design',
1111 + '.dev',
1112 + '.diamonds',
1113 + '.diet',
1114 + '.digital',
1115 + '.direct',
1116 + '.directory',
1117 + '.discount',
1118 + '.doctor',
1119 + '.dog',
1120 + '.domains',
1121 + '.download',
1122 + '.earth',
1123 + '.eco',
1124 + '.education',
1125 + '.email',
1126 + '.energy',
1127 + '.engineer',
1128 + '.engineering',
1129 + '.enterprises',
1130 + '.equipment',
1131 + '.estate',
1132 + '.events',
1133 + '.exchange',
1134 + '.expert',
1135 + '.exposed',
1136 + '.express',
1137 + '.fail',
1138 + '.faith',
1139 + '.family',
1140 + '.fans',
1141 + '.farm',
1142 + '.fashion',
1143 + '.feedback',
1144 + '.film',
1145 + '.finance',
1146 + '.financial',
1147 + '.fish',
1148 + '.fishing',
1149 + '.fit',
1150 + '.fitness',
1151 + '.flights',
1152 + '.florist',
1153 + '.flowers',
1154 + '.football',
1155 + '.forsale',
1156 + '.foundation',
1157 + '.fun',
1158 + '.fund',
1159 + '.furniture',
1160 + '.futbol',
1161 + '.fyi',
1162 + '.gallery',
1163 + '.game',
1164 + '.games',
1165 + '.garden',
1166 + '.gay',
1167 + '.gdn',
1168 + '.gift',
1169 + '.gifts',
1170 + '.gives',
1171 + '.glass',
1172 + '.global',
1173 + '.gmbh',
1174 + '.gold',
1175 + '.golf',
1176 + '.graphics',
1177 + '.gratis',
1178 + '.green',
1179 + '.gripe',
1180 + '.group',
1181 + '.guide',
1182 + '.guitars',
1183 + '.guru',
1184 + '.hamburg',
1185 + '.haus',
1186 + '.health',
1187 + '.healthcare',
1188 + '.help',
1189 + '.hiphop',
1190 + '.hockey',
1191 + '.holdings',
1192 + '.holiday',
1193 + '.horse',
1194 + '.host',
1195 + '.hosting',
1196 + '.house',
1197 + '.how',
1198 + '.icu',
1199 + '.immo',
1200 + '.immobilien',
1201 + '.inc',
1202 + '.industries',
1203 + '.info',
1204 + '.ink',
1205 + '.institute',
1206 + '.insure',
1207 + '.international',
1208 + '.investments',
1209 + '.irish',
1210 + '.jetzt',
1211 + '.jewelry',
1212 + '.juegos',
1213 + '.kaufen',
1214 + '.kim',
1215 + '.kitchen',
1216 + '.kiwi',
1217 + '.krd',
1218 + '.kyoto',
1219 + '.land',
1220 + '.lat',
1221 + '.lawyer',
1222 + '.lease',
1223 + '.legal',
1224 + '.lgbt',
1225 + '.life',
1226 + '.lighting',
1227 + '.limited',
1228 + '.limo',
1229 + '.link',
1230 + '.live',
1231 + '.llc',
1232 + '.loan',
1233 + '.loans',
1234 + '.lol',
1235 + '.london',
1236 + '.love',
1237 + '.ltd',
1238 + '.ltda',
1239 + '.luxury',
1240 + '.maison',
1241 + '.management',
1242 + '.market',
1243 + '.marketing',
1244 + '.mba',
1245 + '.media',
1246 + '.melbourne',
1247 + '.memorial',
1248 + '.men',
1249 + '.menu',
1250 + '.miami',
1251 + '.mobi',
1252 + '.moda',
1253 + '.moe',
1254 + '.mom',
1255 + '.money',
1256 + '.monster',
1257 + '.mortgage',
1258 + '.movie',
1259 + '.nagoya',
1260 + '.name',
1261 + '.navy',
1262 + '.net',
1263 + '.network',
1264 + '.new',
1265 + '.news',
1266 + '.ninja',
1267 + '.nyc',
1268 + '.observer',
1269 + '.okinawa',
1270 + '.one',
1271 + '.onl',
1272 + '.online',
1273 + '.org',
1274 + '.osaka',
1275 + '.page',
1276 + '.paris',
1277 + '.partners',
1278 + '.parts',
1279 + '.party',
1280 + '.photo',
1281 + '.photography',
1282 + '.photos',
1283 + '.pics',
1284 + '.pictures',
1285 + '.pink',
1286 + '.pizza',
1287 + '.place',
1288 + '.plumbing',
1289 + '.plus',
1290 + '.poker',
1291 + '.porn',
1292 + '.press',
1293 + '.pro',
1294 + '.productions',
1295 + '.properties',
1296 + '.property',
1297 + '.protection',
1298 + '.pub',
1299 + '.racing',
1300 + '.realty',
1301 + '.recipes',
1302 + '.red',
1303 + '.rehab',
1304 + '.reise',
1305 + '.reisen',
1306 + '.rent',
1307 + '.rentals',
1308 + '.repair',
1309 + '.report',
1310 + '.republican',
1311 + '.rest',
1312 + '.restaurant',
1313 + '.review',
1314 + '.reviews',
1315 + '.rip',
1316 + '.rocks',
1317 + '.rodeo',
1318 + '.run',
1319 + '.ryukyu',
1320 + '.sale',
1321 + '.sarl',
1322 + '.school',
1323 + '.schule',
1324 + '.science',
1325 + '.security',
1326 + '.services',
1327 + '.sex',
1328 + '.sexy',
1329 + '.shiksha',
1330 + '.shoes',
1331 + '.shop',
1332 + '.shopping',
1333 + '.show',
1334 + '.singles',
1335 + '.site',
1336 + '.ski',
1337 + '.soccer',
1338 + '.social',
1339 + '.software',
1340 + '.solar',
1341 + '.solutions',
1342 + '.soy',
1343 + '.space',
1344 + '.storage',
1345 + '.store',
1346 + '.stream',
1347 + '.studio',
1348 + '.study',
1349 + '.style',
1350 + '.sucks',
1351 + '.supplies',
1352 + '.supply',
1353 + '.support',
1354 + '.surf',
1355 + '.surgery',
1356 + '.sydney',
1357 + '.systems',
1358 + '.tattoo',
1359 + '.tax',
1360 + '.taxi',
1361 + '.team',
1362 + '.tech',
1363 + '.technology',
1364 + '.tel',
1365 + '.tennis',
1366 + '.theater',
1367 + '.theatre',
1368 + '.tienda',
1369 + '.tips',
1370 + '.tires',
1371 + '.today',
1372 + '.tokyo',
1373 + '.tools',
1374 + '.top',
1375 + '.tours',
1376 + '.town',
1377 + '.toys',
1378 + '.trade',
1379 + '.training',
1380 + '.travel',
1381 + '.tube',
1382 + '.university',
1383 + '.uno',
1384 + '.vacations',
1385 + '.vegas',
1386 + '.ventures',
1387 + '.vet',
1388 + '.viajes',
1389 + '.video',
1390 + '.villas',
1391 + '.vin',
1392 + '.vip',
1393 + '.vision',
1394 + '.vodka',
1395 + '.vote',
1396 + '.voting',
1397 + '.voto',
1398 + '.voyage',
1399 + '.wales',
1400 + '.watch',
1401 + '.webcam',
1402 + '.website',
1403 + '.wedding',
1404 + '.wiki',
1405 + '.win',
1406 + '.wine',
1407 + '.work',
1408 + '.works',
1409 + '.world',
1410 + '.wtf',
1411 + '.xn--3ds443g',
1412 + '.xn--6frz82g',
1413 + '.xxx',
1414 + '.xyz',
1415 + '.yoga',
1416 + '.yokohama',
1417 + '.zone',
1418 + );
1419 +
1420 + $host = ! empty( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
1421 +
1422 + // get the domain.
1423 + $domain = explode( '.', $host );
1424 + if ( count( $domain ) >= 2 ) {
1425 + $domain = '.' . $domain[ count( $domain ) - 1 ];
1426 + } else {
1427 + $domain = null;
1428 + }
1429 +
1430 + return ! in_array( $domain, $valid_domains, true );
1431 + }
1432 +}