PluginProbe
Elementor Website Builder – more than just a page builder / 3.0.2
Elementor Website Builder – more than just a page builder v3.0.2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
← All changes | includes/utils.php +56 -474 4.2.43.0.2 View file →
@@ -1,10 +1,7 @@
1 1 <?php
2 2 namespace Elementor;
3 3
4 -use Elementor\Core\Files\Fonts\Google_Font;
5 -use Elementor\Core\Utils\Collection;
6 -
7 4 if ( ! defined( 'ABSPATH' ) ) {
8 5 exit; // Exit if accessed directly.
9 6 }
10 7
@@ -19,107 +16,29 @@
19 16 class Utils {
20 17
21 18 const DEPRECATION_RANGE = 0.4;
22 19
23 - const EDITOR_BREAK_LINES_OPTION_KEY = 'elementor_editor_break_lines';
24 -
25 20 /**
26 - * A list of safe tags for `validate_html_tag` method.
27 - */
28 - const ALLOWED_HTML_WRAPPER_TAGS = [
29 - 'a',
30 - 'article',
31 - 'aside',
32 - 'button',
33 - 'form',
34 - 'div',
35 - 'footer',
36 - 'h1',
37 - 'h2',
38 - 'h3',
39 - 'h4',
40 - 'h5',
41 - 'h6',
42 - 'header',
43 - 'main',
44 - 'nav',
45 - 'p',
46 - 'section',
47 - 'span',
48 - ];
49 -
50 - const EXTENDED_ALLOWED_HTML_TAGS = [
51 - 'iframe' => [
52 - 'iframe' => [
53 - 'allow' => true,
54 - 'allowfullscreen' => true,
55 - 'frameborder' => true,
56 - 'height' => true,
57 - 'loading' => true,
58 - 'name' => true,
59 - 'referrerpolicy' => true,
60 - 'sandbox' => true,
61 - 'src' => true,
62 - 'width' => true,
63 - ],
64 - ],
65 - 'svg' => [
66 - 'svg' => [
67 - 'aria-hidden' => true,
68 - 'aria-labelledby' => true,
69 - 'class' => true,
70 - 'height' => true,
71 - 'role' => true,
72 - 'viewbox' => true,
73 - 'width' => true,
74 - 'xmlns' => true,
75 - ],
76 - 'g' => [
77 - 'fill' => true,
78 - ],
79 - 'title' => [
80 - 'title' => true,
81 - ],
82 - 'path' => [
83 - 'd' => true,
84 - 'fill' => true,
85 - ],
86 - ],
87 - 'image' => [
88 - 'img' => [
89 - 'srcset' => true,
90 - 'sizes' => true,
91 - ],
92 - ],
93 - ];
94 -
95 - /**
96 - * Variables for free to pro upsale modal promotions
97 - */
98 -
99 - const ANIMATED_HEADLINE = 'animated_headline';
100 -
101 - const CTA = 'cta';
102 -
103 - const VIDEO_PLAYLIST = 'video_playlist';
104 -
105 - const TESTIMONIAL_WIDGET = 'testimonial_widget';
106 -
107 - const IMAGE_CAROUSEL = 'image_carousel';
108 -
109 - /**
110 - * Whether WordPress CLI mode is enabled or not.
21 + * Is ajax.
111 22 *
23 + * Whether the current request is a WordPress ajax request.
24 + *
25 + * @since 1.0.0
26 + * @deprecated 2.6.0 Use `wp_doing_ajax()` instead.
112 27 * @access public
113 28 * @static
114 29 *
115 - * @return bool
30 + * @return bool True if it's a WordPress ajax request, false otherwise.
116 31 */
117 - public static function is_wp_cli() {
118 - return defined( 'WP_CLI' ) && WP_CLI;
32 + public static function is_ajax() {
33 + _deprecated_function( __METHOD__, '2.6.0', 'wp_doing_ajax()' );
34 +
35 + return wp_doing_ajax();
119 36 }
120 37
121 38 /**
39 + * Is script debug.
40 + *
122 41 * Whether script debug is enabled or not.
123 42 *
124 43 * @since 1.0.0
125 44 * @access public
@@ -124,9 +43,9 @@
124 43 * @since 1.0.0
125 44 * @access public
126 45 * @static
127 46 *
128 - * @return bool
47 + * @return bool True if it's a script debug is active, false otherwise.
129 48 */
130 49 public static function is_script_debug() {
131 50 return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
132 51 }
@@ -131,32 +50,8 @@
131 50 return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
132 51 }
133 52
134 53 /**
135 - * Whether Elementor debug is enabled or not.
136 - *
137 - * @access public
138 - * @static
139 - *
140 - * @return bool
141 - */
142 - public static function is_elementor_debug() {
143 - return defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG;
144 - }
145 -
146 - /**
147 - * Whether Elementor test mode is enabled or not.
148 - *
149 - * @access public
150 - * @static
151 - *
152 - * @return bool
153 - */
154 - public static function is_elementor_tests() {
155 - return defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_TESTS;
156 - }
157 -
158 - /**
159 54 * Get pro link.
160 55 *
161 56 * Retrieve the link to Elementor Pro.
162 57 *
@@ -195,65 +90,45 @@
195 90 * @since 2.1.0
196 91 * @static
197 92 * @access public
198 93 *
199 - * @param string $from
200 - * @param string $to
94 + * @param $from
95 + * @param $to
201 96 *
202 97 * @return string
203 - * @throws \Exception If URLs are missing or invalid URLs provided.
98 + * @throws \Exception
204 99 */
205 100 public static function replace_urls( $from, $to ) {
206 101 $from = trim( $from );
207 102 $to = trim( $to );
208 103
209 - if ( empty( $from ) ) {
210 - throw new \Exception( 'Couldn’t replace your address because the old URL was not provided. Try again by entering the old URL.' );
211 - }
212 -
213 - if ( empty( $to ) ) {
214 - throw new \Exception( 'Couldn’t replace your address because the new URL was not provided. Try again by entering the new URL.' );
215 - }
216 -
217 104 if ( $from === $to ) {
218 - throw new \Exception( 'Couldn’t replace your address because both of the URLs provided are identical. Try again by entering different URLs.' );
105 + throw new \Exception( __( 'The `from` and `to` URL\'s must be different', 'elementor' ) );
219 106 }
220 107
221 108 $is_valid_urls = ( filter_var( $from, FILTER_VALIDATE_URL ) && filter_var( $to, FILTER_VALIDATE_URL ) );
222 -
223 109 if ( ! $is_valid_urls ) {
224 - throw new \Exception( 'Couldn’t replace your address because at least one of the URLs provided are invalid. Try again by entering valid URLs.' );
110 + throw new \Exception( __( 'The `from` and `to` URL\'s must be valid URL\'s', 'elementor' ) );
225 111 }
226 112
227 113 global $wpdb;
228 - $escaped_from = str_replace( '/', '\\/', $from );
229 - $escaped_to = str_replace( '/', '\\/', $to );
230 - $meta_value_like = '[%'; // meta_value LIKE '[%' are json formatted
231 114
115 + // @codingStandardsIgnoreStart cannot use `$wpdb->prepare` because it remove's the backslashes
232 116 $rows_affected = $wpdb->query(
233 - $wpdb->prepare(
234 - "UPDATE {$wpdb->postmeta} " .
235 - 'SET `meta_value` = REPLACE(`meta_value`, %s, %s) ' .
236 - "WHERE `meta_key` = '_elementor_data' AND `meta_value` LIKE %s;",
237 - $escaped_from,
238 - $escaped_to,
239 - $meta_value_like
240 - )
241 - );
117 + "UPDATE {$wpdb->postmeta} " .
118 + "SET `meta_value` = REPLACE(`meta_value`, '" . str_replace( '/', '\\\/', $from ) . "', '" . str_replace( '/', '\\\/', $to ) . "') " .
119 + "WHERE `meta_key` = '_elementor_data' AND `meta_value` LIKE '[%' ;" ); // meta_value LIKE '[%' are json formatted
120 + // @codingStandardsIgnoreEnd
242 121
243 122 if ( false === $rows_affected ) {
244 - throw new \Exception( 'An error occurred while replacing URL\'s.' );
123 + throw new \Exception( __( 'An error occurred', 'elementor' ) );
245 124 }
246 125
247 - // Allow externals to replace-urls, when they have to.
248 - $rows_affected += (int) apply_filters( 'elementor/tools/replace-urls', 0, $from, $to );
249 -
250 126 Plugin::$instance->files_manager->clear_cache();
251 - Google_Font::clear_cache();
252 127
253 128 return sprintf(
254 - /* translators: %d: Number of rows. */
255 - _n( '%d database row affected.', '%d database rows affected.', $rows_affected, 'elementor' ),
129 + /* translators: %d: Number of rows */
130 + _n( '%d row affected.', '%d rows affected.', $rows_affected, 'elementor' ),
256 131 $rows_affected
257 132 );
258 133 }
259 134
@@ -280,9 +155,9 @@
280 155 *
281 156 * Filters whether the post type supports editing with Elementor.
282 157 *
283 158 * @since 1.0.0
284 - * @deprecated 2.2.0 Use `elementor/utils/is_post_support` hook Instead.
159 + * @deprecated 2.2.0 Use `elementor/utils/is_post_support` Instead
285 160 *
286 161 * @param bool $is_supported Whether the post type supports editing with Elementor.
287 162 * @param int $post_id Post ID.
288 163 * @param string $post_type Post type.
@@ -399,10 +274,10 @@
399 274 if ( ! defined( 'DONOTCDN' ) ) {
400 275 define( 'DONOTCDN', true );
401 276 }
402 277
403 - if ( ! defined( 'DONOTCACHEOBJECT' ) ) {
404 - define( 'DONOTCACHEOBJECT', true );
278 + if ( ! defined( 'DONOTCACHCEOBJECT' ) ) {
279 + define( 'DONOTCACHCEOBJECT', true );
405 280 }
406 281
407 282 // Set the headers to prevent caching for the different browsers.
408 283 nocache_headers();
@@ -441,20 +316,23 @@
441 316 * Retrieve a custom URL for creating a new post/page using Elementor.
442 317 *
443 318 * @since 1.9.0
444 319 * @access public
445 - * @deprecated 3.3.0 Use `Plugin::$instance->documents->get_create_new_post_url()` instead.
446 320 * @static
447 321 *
448 - * @param string $post_type Optional. Post type slug. Default is 'page'.
449 - * @param string|null $template_type Optional. Query arg 'template_type'. Default is null.
322 + * @param string $post_type Optional. Post type slug. Default is 'page'.
450 323 *
451 324 * @return string A URL for creating new post using Elementor.
452 325 */
453 - public static function get_create_new_post_url( $post_type = 'page', $template_type = null ) {
454 - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __FUNCTION__, '3.3.0', 'Plugin::$instance->documents->get_create_new_post_url()' );
326 + public static function get_create_new_post_url( $post_type = 'page' ) {
327 + $new_post_url = add_query_arg( [
328 + 'action' => 'elementor_new_post',
329 + 'post_type' => $post_type,
330 + ], admin_url( 'edit.php' ) );
455 331
456 - return Plugin::$instance->documents->get_create_new_post_url( $post_type, $template_type );
332 + $new_post_url = add_query_arg( '_wpnonce', wp_create_nonce( 'elementor_action_new_post' ), $new_post_url );
333 +
334 + return $new_post_url;
457 335 }
458 336
459 337 /**
460 338 * Get post autosave.
@@ -513,14 +391,14 @@
513 391 * @since 2.1.2
514 392 * @access public
515 393 * @static
516 394 */
517 - public static function array_inject( $base_array, $key, $insert ) {
518 - $length = array_search( $key, array_keys( $base_array ), true ) + 1;
395 + public static function array_inject( $array, $key, $insert ) {
396 + $length = array_search( $key, array_keys( $array ), true ) + 1;
519 397
520 - return array_slice( $base_array, 0, $length, true ) +
398 + return array_slice( $array, 0, $length, true ) +
521 399 $insert +
522 - array_slice( $base_array, $length, null, true );
400 + array_slice( $array, $length, null, true );
523 401 }
524 402
525 403 /**
526 404 * Render html attributes
@@ -544,39 +422,20 @@
544 422
545 423 return implode( ' ', $rendered_attributes );
546 424 }
547 425
548 - /**
549 - * Safe print html attributes
550 - *
551 - * @access public
552 - * @static
553 - * @param array $attributes
554 - */
555 - public static function print_html_attributes( array $attributes ) {
556 - // PHPCS - the method render_html_attributes is safe.
557 - echo self::render_html_attributes( $attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
558 - }
559 -
560 426 public static function get_meta_viewport( $context = '' ) {
561 427 $meta_tag = '<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />';
562 -
563 428 /**
564 429 * Viewport meta tag.
565 430 *
566 - * Filters the meta tag containing the viewport information.
431 + * Filters the Elementor preview URL.
567 432 *
568 - * This hook can be used to change the initial viewport meta tag set by Elementor
569 - * and replace it with a different viewport tag.
570 - *
571 433 * @since 2.5.0
572 434 *
573 435 * @param string $meta_tag Viewport meta tag.
574 - * @param string $context Page context.
575 436 */
576 - $meta_tag = apply_filters( 'elementor/template/viewport_tag', $meta_tag, $context );
577 -
578 - return $meta_tag;
437 + return apply_filters( 'elementor/template/viewport_tag', $meta_tag, $context );
579 438 }
580 439
581 440 /**
582 441 * Add Elementor Config js vars to the relevant script handle,
@@ -581,17 +440,16 @@
581 440 /**
582 441 * Add Elementor Config js vars to the relevant script handle,
583 442 * WP will wrap it with <script> tag.
584 443 * To make sure this script runs thru the `script_loader_tag` hook, use a known handle value.
585 - *
586 444 * @param string $handle
587 445 * @param string $js_var
588 - * @param mixed $config
446 + * @param mixed $config
589 447 */
590 448 public static function print_js_config( $handle, $js_var, $config ) {
591 449 $config = wp_json_encode( $config );
592 450
593 - if ( get_option( self::EDITOR_BREAK_LINES_OPTION_KEY ) ) {
451 + if ( get_option( 'elementor_editor_break_lines' ) ) {
594 452 // Add new lines to avoid memory limits in some hosting servers that handles the buffer output according to new line characters
595 453 $config = str_replace( '}},"', '}},' . PHP_EOL . '"', $config );
596 454 }
597 455
@@ -609,9 +467,9 @@
609 467
610 468 $alias_version_as_float = (float) $alias_version[0];
611 469
612 470 if ( round( $current_version_as_float - $alias_version_as_float, 1 ) >= self::DEPRECATION_RANGE ) {
613 - _deprecated_file( $item, $version, $replacement ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
471 + _deprecated_file( $item, $version, $replacement );
614 472 }
615 473 }
616 474
617 475 /**
@@ -616,9 +474,9 @@
616 474
617 475 /**
618 476 * Checks a control value for being empty, including a string of '0' not covered by PHP's empty().
619 477 *
620 - * @param mixed $source
478 + * @param mixed $source
621 479 * @param bool|string $key
622 480 *
623 481 * @return bool
624 482 */
@@ -637,34 +495,15 @@
637 495 public static function has_pro() {
638 496 return defined( 'ELEMENTOR_PRO_VERSION' );
639 497 }
640 498
641 - public static function is_license_active(): bool {
642 - return class_exists( '\ElementorPro\License\API' ) && \ElementorPro\License\API::is_license_active();
643 - }
644 -
645 - public static function is_pro_installed_and_not_active(): bool {
646 - if ( ! function_exists( 'get_plugins' ) ) {
647 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
648 - }
649 -
650 - $file_path = self::get_elementor_pro_file_path();
651 - $installed_plugins = get_plugins();
652 -
653 - return isset( $installed_plugins[ $file_path ] );
654 - }
655 -
656 - private static function get_elementor_pro_file_path(): string {
657 - return 'elementor-pro/elementor-pro.php';
658 - }
659 -
660 499 /**
661 500 * Convert HTMLEntities to UTF-8 characters
662 501 *
663 - * @param string $html_string
502 + * @param $string
664 503 * @return string
665 504 */
666 - public static function urlencode_html_entities( $html_string ) {
505 + public static function urlencode_html_entities( $string ) {
667 506 $entities_dictionary = [
668 507 '&#145;' => "'", // Opening single quote
669 508 '&#146;' => "'", // Closing single quote
670 509 '&#147;' => '"', // Closing double quote
@@ -677,11 +516,11 @@
677 516 '&#8222;' => '"', // Double low quote
678 517 ];
679 518
680 519 // Decode decimal entities
681 - $html_string = str_replace( array_keys( $entities_dictionary ), array_values( $entities_dictionary ), $html_string );
520 + $string = str_replace( array_keys( $entities_dictionary ), array_values( $entities_dictionary ), $string );
682 521
683 - return rawurlencode( html_entity_decode( $html_string, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
522 + return rawurlencode( html_entity_decode( $string, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
684 523 }
685 524
686 525 /**
687 526 * Parse attributes that come as a string of comma-delimited key|value pairs.
@@ -733,273 +572,16 @@
733 572 if ( $id === $element['id'] ) {
734 573 return $element;
735 574 }
736 575
737 - $inner_elements = apply_filters(
738 - 'elementor/utils/find_element_recursive/inner_elements',
739 - $element['elements'] ?? [],
740 - $element
741 - );
576 + if ( ! empty( $element['elements'] ) ) {
577 + $element = self::find_element_recursive( $element['elements'], $id );
742 578
743 - if ( ! empty( $inner_elements ) ) {
744 - $found = self::find_element_recursive( $inner_elements, $id );
745 -
746 - if ( $found ) {
747 - return $found;
579 + if ( $element ) {
580 + return $element;
748 581 }
749 582 }
750 583 }
751 584
752 585 return false;
753 - }
754 -
755 - /**
756 - * Change Submenu First Item Label
757 - *
758 - * Overwrite the label of the first submenu item of an admin menu item.
759 - *
760 - * Fired by `admin_menu` action.
761 - *
762 - * @since 3.1.0
763 - *
764 - * @param string $menu_slug
765 - * @param string $new_label
766 - * @access public
767 - */
768 - public static function change_submenu_first_item_label( $menu_slug, $new_label ) {
769 - global $submenu;
770 -
771 - if ( isset( $submenu[ $menu_slug ] ) ) {
772 - // @codingStandardsIgnoreStart
773 - $submenu[ $menu_slug ][0][0] = $new_label;
774 - // @codingStandardsIgnoreEnd
775 - }
776 - }
777 -
778 - /**
779 - * Validate an HTML tag against a safe allowed list.
780 - *
781 - * @param string $tag
782 - *
783 - * @return string
784 - */
785 - public static function validate_html_tag( $tag ) {
786 - return $tag && in_array( strtolower( $tag ), self::ALLOWED_HTML_WRAPPER_TAGS ) ? $tag : 'div';
787 - }
788 -
789 - /**
790 - * Safe print a validated HTML tag.
791 - *
792 - * @param string $tag
793 - */
794 - public static function print_validated_html_tag( $tag ) {
795 - // PHPCS - the method validate_html_tag is safe.
796 - echo self::validate_html_tag( $tag ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
797 - }
798 -
799 - /**
800 - * Print internal content (not user input) without escaping.
801 - */
802 - public static function print_unescaped_internal_string( $internal_string ) {
803 - echo $internal_string; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
804 - }
805 -
806 - /**
807 - * Get recently edited posts query.
808 - *
809 - * Returns `WP_Query` of the recent edited posts.
810 - * By default max posts ( $args['posts_per_page'] ) is 3.
811 - *
812 - * @param array $args
813 - *
814 - * @return \WP_Query
815 - */
816 - public static function get_recently_edited_posts_query( $args = [] ) {
817 - $args = wp_parse_args( $args, [
818 - 'no_found_rows' => true,
819 - 'post_type' => 'any',
820 - 'post_status' => [ 'publish', 'draft' ],
821 - 'posts_per_page' => '3',
822 - 'meta_key' => '_elementor_edit_mode',
823 - 'meta_value' => 'builder',
824 - 'orderby' => 'modified',
825 - ] );
826 -
827 - return new \WP_Query( $args );
828 - }
829 -
830 - public static function print_wp_kses_extended( $text, array $tags ) {
831 - $allowed_html = wp_kses_allowed_html( 'post' );
832 -
833 - foreach ( $tags as $tag ) {
834 - if ( isset( self::EXTENDED_ALLOWED_HTML_TAGS[ $tag ] ) ) {
835 - $extended_tags = apply_filters( "elementor/extended_allowed_html_tags/{$tag}", self::EXTENDED_ALLOWED_HTML_TAGS[ $tag ] );
836 - $allowed_html = array_replace_recursive( $allowed_html, $extended_tags );
837 - }
838 - }
839 -
840 - echo wp_kses( $text, $allowed_html );
841 - }
842 -
843 - public static function kses_post_deep( $data ) {
844 - return map_deep( $data, function ( $value ) {
845 - return is_string( $value ) ? wp_kses_post( $value ) : $value;
846 - } );
847 - }
848 -
849 - public static function is_elementor_path( $path ) {
850 - $path = wp_normalize_path( $path );
851 -
852 - /**
853 - * Elementor related paths.
854 - *
855 - * Filters Elementor related paths.
856 - *
857 - * @param string[] $available_paths
858 - */
859 - $available_paths = apply_filters( 'elementor/utils/elementor_related_paths', [ ELEMENTOR_PATH ] );
860 -
861 - return (bool) ( new Collection( $available_paths ) )
862 - ->map( function ( $p ) {
863 - // `untrailingslashit` in order to include other plugins prefixed with elementor.
864 - return untrailingslashit( wp_normalize_path( $p ) );
865 - } )
866 - ->find(function ( $p ) use ( $path ) {
867 - return false !== strpos( $path, $p );
868 - } );
869 - }
870 -
871 - /**
872 - * @param string $file
873 - * @param mixed ...$args
874 - * @return false|string
875 - */
876 - public static function file_get_contents( $file, ...$args ) {
877 - if ( ! is_file( $file ) || ! is_readable( $file ) ) {
878 - return false;
879 - }
880 - return file_get_contents( $file, ...$args );
881 - }
882 -
883 - public static function get_super_global_value( $super_global, $key ) {
884 - if ( ! isset( $super_global[ $key ] ) ) {
885 - return null;
886 - }
887 -
888 - if ( $_FILES === $super_global ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
889 - return isset( $super_global[ $key ]['name'] ) ?
890 - self::sanitize_file_name( $super_global[ $key ] ) :
891 - self::sanitize_multi_upload( $super_global[ $key ] );
892 - }
893 -
894 - return wp_kses_post_deep( wp_unslash( $super_global[ $key ] ) );
895 - }
896 -
897 - private static function sanitize_multi_upload( $fields ) {
898 - return array_map( function( $field ) {
899 - return array_map( 'self::sanitize_file_name', $field );
900 - }, $fields );
901 - }
902 -
903 - private static function sanitize_file_name( $file ) {
904 - $file['name'] = sanitize_file_name( $file['name'] );
905 -
906 - return $file;
907 - }
908 -
909 - /**
910 - * Return specific object property value if exist from array of keys.
911 - *
912 - * @param array $base_array
913 - * @param array $keys
914 - * @return mixed|null
915 - */
916 - public static function get_array_value_by_keys( $base_array, $keys ) {
917 - $keys = (array) $keys;
918 - foreach ( $keys as $key ) {
919 - if ( ! isset( $base_array[ $key ] ) ) {
920 - return null;
921 - }
922 - $base_array = $base_array[ $key ];
923 - }
924 - return $base_array;
925 - }
926 -
927 - public static function get_cached_callback( $callback, $cache_key, $cache_time = 24 * HOUR_IN_SECONDS ) {
928 - $cache = get_site_transient( $cache_key );
929 -
930 - if ( ! $cache ) {
931 - $cache = call_user_func( $callback );
932 -
933 - if ( ! is_wp_error( $cache ) ) {
934 - set_site_transient( $cache_key, $cache, $cache_time );
935 - }
936 - }
937 -
938 - return $cache;
939 - }
940 -
941 - public static function is_sale_time(): bool {
942 - $sale_start_time = gmmktime( 10, 0, 0, 6, 15, 2026 );
943 - $sale_end_time = gmmktime( 3, 59, 0, 6, 17, 2026 );
944 -
945 - $now_time = gmdate( 'U' );
946 -
947 - return $now_time >= $sale_start_time && $now_time <= $sale_end_time;
948 - }
949 -
950 - public static function safe_throw( string $message ) {
951 - if ( ! static::is_elementor_debug() ) {
952 - return;
953 - }
954 -
955 - throw new \Exception( esc_html( $message ) );
956 - }
957 -
958 - public static function has_invalid_post_permissions( $post ): bool {
959 - $is_image_attachment = 'attachment' === $post->post_type && strpos( $post->post_mime_type, 'image/' ) === 0;
960 -
961 - if ( $is_image_attachment ) {
962 - return false;
963 - }
964 -
965 - $is_private = 'private' === $post->post_status
966 - && ! current_user_can( 'read_private_posts', $post->ID );
967 -
968 - $not_allowed = 'publish' !== $post->post_status
969 - && ! current_user_can( 'edit_post', $post->ID );
970 -
971 - $password_required = post_password_required( $post->ID )
972 - && ! current_user_can( 'edit_post', $post->ID );
973 -
974 - return $is_private || $not_allowed || $password_required;
975 - }
976 -
977 - public static function is_custom_kit_applied() {
978 - return (bool) Plugin::$instance->kits_manager->get_previous_id();
979 - }
980 -
981 - public static function decode_string( string $encoded_string, ?string $fallback = '' ) {
982 - try {
983 - return base64_decode( $encoded_string, true ) ?? $fallback;
984 - } catch ( \Exception $e ) {
985 - return $fallback;
986 - }
987 - }
988 -
989 - public static function encode_string( string $decoded_string ): string {
990 - return base64_encode( $decoded_string );
991 - }
992 -
993 - public static function html_to_plain_text( string $html ): string {
994 - if ( empty( $html ) ) {
995 - return '';
996 - }
997 -
998 - $text = preg_replace( '#<br\s*/?\s*>#i', ' ', $html );
999 - $text = preg_replace( '#</?[a-z][^>]*>#i', ' ', $text );
1000 - $text = html_entity_decode( $text, ENT_QUOTES, 'UTF-8' );
1001 - $text = str_replace( "\xE2\x80\x8B", '', $text );
1002 -
1003 - return trim( preg_replace( '/\s+/', ' ', $text ) );
1004 586 }
1005 587 }