PluginProbe
Elementor Website Builder – more than just a page builder / 3.1.2
Elementor Website Builder – more than just a page builder v3.1.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
elementor / includes / utils.php

utils.php in Elementor Website Builder – more than just a page builder 3.1.2, at includes/utils.php

627 lines 15.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit; // Exit if accessed directly.
6 }
7
8 /**
9 * Elementor utils.
10 *
11 * Elementor utils handler class is responsible for different utility methods
12 * used by Elementor.
13 *
14 * @since 1.0.0
15 */
16 class Utils {
17
18 const DEPRECATION_RANGE = 0.4;
19
20 /**
21 * Is ajax.
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.
27 * @access public
28 * @static
29 *
30 * @return bool True if it's a WordPress ajax request, false otherwise.
31 */
32 public static function is_ajax() {
33 _deprecated_function( __METHOD__, '2.6.0', 'wp_doing_ajax()' );
34
35 return wp_doing_ajax();
36 }
37
38 /**
39 * Is WP CLI.
40 *
41 * @return bool
42 */
43 public static function is_wp_cli() {
44 return defined( 'WP_CLI' ) && WP_CLI;
45 }
46
47 /**
48 * Is script debug.
49 *
50 * Whether script debug is enabled or not.
51 *
52 * @since 1.0.0
53 * @access public
54 * @static
55 *
56 * @return bool True if it's a script debug is active, false otherwise.
57 */
58 public static function is_script_debug() {
59 return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
60 }
61
62 /**
63 * Get pro link.
64 *
65 * Retrieve the link to Elementor Pro.
66 *
67 * @since 1.7.0
68 * @access public
69 * @static
70 *
71 * @param string $link URL to Elementor pro.
72 *
73 * @return string Elementor pro link.
74 */
75 public static function get_pro_link( $link ) {
76 static $theme_name = false;
77
78 if ( ! $theme_name ) {
79 $theme_obj = wp_get_theme();
80 if ( $theme_obj->parent() ) {
81 $theme_name = $theme_obj->parent()->get( 'Name' );
82 } else {
83 $theme_name = $theme_obj->get( 'Name' );
84 }
85
86 $theme_name = sanitize_key( $theme_name );
87 }
88
89 $link = add_query_arg( 'utm_term', $theme_name, $link );
90
91 return $link;
92 }
93
94 /**
95 * Replace URLs.
96 *
97 * Replace old URLs to new URLs. This method also updates all the Elementor data.
98 *
99 * @since 2.1.0
100 * @static
101 * @access public
102 *
103 * @param $from
104 * @param $to
105 *
106 * @return string
107 * @throws \Exception
108 */
109 public static function replace_urls( $from, $to ) {
110 $from = trim( $from );
111 $to = trim( $to );
112
113 if ( $from === $to ) {
114 throw new \Exception( __( 'The `from` and `to` URL\'s must be different', 'elementor' ) );
115 }
116
117 $is_valid_urls = ( filter_var( $from, FILTER_VALIDATE_URL ) && filter_var( $to, FILTER_VALIDATE_URL ) );
118 if ( ! $is_valid_urls ) {
119 throw new \Exception( __( 'The `from` and `to` URL\'s must be valid URL\'s', 'elementor' ) );
120 }
121
122 global $wpdb;
123
124 // @codingStandardsIgnoreStart cannot use `$wpdb->prepare` because it remove's the backslashes
125 $rows_affected = $wpdb->query(
126 "UPDATE {$wpdb->postmeta} " .
127 "SET `meta_value` = REPLACE(`meta_value`, '" . str_replace( '/', '\\\/', $from ) . "', '" . str_replace( '/', '\\\/', $to ) . "') " .
128 "WHERE `meta_key` = '_elementor_data' AND `meta_value` LIKE '[%' ;" ); // meta_value LIKE '[%' are json formatted
129 // @codingStandardsIgnoreEnd
130
131 if ( false === $rows_affected ) {
132 throw new \Exception( __( 'An error occurred', 'elementor' ) );
133 }
134
135 Plugin::$instance->files_manager->clear_cache();
136
137 return sprintf(
138 /* translators: %d: Number of rows */
139 _n( '%d row affected.', '%d rows affected.', $rows_affected, 'elementor' ),
140 $rows_affected
141 );
142 }
143
144 /**
145 * Is post supports Elementor.
146 *
147 * Whether the post supports editing with Elementor.
148 *
149 * @since 1.0.0
150 * @access public
151 * @static
152 *
153 * @param int $post_id Optional. Post ID. Default is `0`.
154 *
155 * @return string True if post supports editing with Elementor, false otherwise.
156 */
157 public static function is_post_support( $post_id = 0 ) {
158 $post_type = get_post_type( $post_id );
159
160 $is_supported = self::is_post_type_support( $post_type );
161
162 /**
163 * Is post type support.
164 *
165 * Filters whether the post type supports editing with Elementor.
166 *
167 * @since 1.0.0
168 * @deprecated 2.2.0 Use `elementor/utils/is_post_support` Instead
169 *
170 * @param bool $is_supported Whether the post type supports editing with Elementor.
171 * @param int $post_id Post ID.
172 * @param string $post_type Post type.
173 */
174 $is_supported = apply_filters( 'elementor/utils/is_post_type_support', $is_supported, $post_id, $post_type );
175
176 /**
177 * Is post support.
178 *
179 * Filters whether the post supports editing with Elementor.
180 *
181 * @since 2.2.0
182 *
183 * @param bool $is_supported Whether the post type supports editing with Elementor.
184 * @param int $post_id Post ID.
185 * @param string $post_type Post type.
186 */
187 $is_supported = apply_filters( 'elementor/utils/is_post_support', $is_supported, $post_id, $post_type );
188
189 return $is_supported;
190 }
191
192
193 /**
194 * Is post type supports Elementor.
195 *
196 * Whether the post type supports editing with Elementor.
197 *
198 * @since 2.2.0
199 * @access public
200 * @static
201 *
202 * @param string $post_type Post Type.
203 *
204 * @return string True if post type supports editing with Elementor, false otherwise.
205 */
206 public static function is_post_type_support( $post_type ) {
207 if ( ! post_type_exists( $post_type ) ) {
208 return false;
209 }
210
211 if ( ! post_type_supports( $post_type, 'elementor' ) ) {
212 return false;
213 }
214
215 return true;
216 }
217
218 /**
219 * Get placeholder image source.
220 *
221 * Retrieve the source of the placeholder image.
222 *
223 * @since 1.0.0
224 * @access public
225 * @static
226 *
227 * @return string The source of the default placeholder image used by Elementor.
228 */
229 public static function get_placeholder_image_src() {
230 $placeholder_image = ELEMENTOR_ASSETS_URL . 'images/placeholder.png';
231
232 /**
233 * Get placeholder image source.
234 *
235 * Filters the source of the default placeholder image used by Elementor.
236 *
237 * @since 1.0.0
238 *
239 * @param string $placeholder_image The source of the default placeholder image.
240 */
241 $placeholder_image = apply_filters( 'elementor/utils/get_placeholder_image_src', $placeholder_image );
242
243 return $placeholder_image;
244 }
245
246 /**
247 * Generate random string.
248 *
249 * Returns a string containing a hexadecimal representation of random number.
250 *
251 * @since 1.0.0
252 * @access public
253 * @static
254 *
255 * @return string Random string.
256 */
257 public static function generate_random_string() {
258 return dechex( rand() );
259 }
260
261 /**
262 * Do not cache.
263 *
264 * Tell WordPress cache plugins not to cache this request.
265 *
266 * @since 1.0.0
267 * @access public
268 * @static
269 */
270 public static function do_not_cache() {
271 if ( ! defined( 'DONOTCACHEPAGE' ) ) {
272 define( 'DONOTCACHEPAGE', true );
273 }
274
275 if ( ! defined( 'DONOTCACHEDB' ) ) {
276 define( 'DONOTCACHEDB', true );
277 }
278
279 if ( ! defined( 'DONOTMINIFY' ) ) {
280 define( 'DONOTMINIFY', true );
281 }
282
283 if ( ! defined( 'DONOTCDN' ) ) {
284 define( 'DONOTCDN', true );
285 }
286
287 if ( ! defined( 'DONOTCACHCEOBJECT' ) ) {
288 define( 'DONOTCACHCEOBJECT', true );
289 }
290
291 // Set the headers to prevent caching for the different browsers.
292 nocache_headers();
293 }
294
295 /**
296 * Get timezone string.
297 *
298 * Retrieve timezone string from the WordPress database.
299 *
300 * @since 1.0.0
301 * @access public
302 * @static
303 *
304 * @return string Timezone string.
305 */
306 public static function get_timezone_string() {
307 $current_offset = (float) get_option( 'gmt_offset' );
308 $timezone_string = get_option( 'timezone_string' );
309
310 // Create a UTC+- zone if no timezone string exists.
311 if ( empty( $timezone_string ) ) {
312 if ( $current_offset < 0 ) {
313 $timezone_string = 'UTC' . $current_offset;
314 } else {
315 $timezone_string = 'UTC+' . $current_offset;
316 }
317 }
318
319 return $timezone_string;
320 }
321
322 /**
323 * Get create new post URL.
324 *
325 * Retrieve a custom URL for creating a new post/page using Elementor.
326 *
327 * @since 1.9.0
328 * @access public
329 * @static
330 *
331 * @param string $post_type Optional. Post type slug. Default is 'page'.
332 * @param string|null $template_type Optional. Query arg 'template_type'. Default is null.
333 *
334 * @return string A URL for creating new post using Elementor.
335 */
336 public static function get_create_new_post_url( $post_type = 'page', $template_type = null ) {
337 $query_args = [
338 'action' => 'elementor_new_post',
339 'post_type' => $post_type,
340 ];
341
342 if ( $template_type ) {
343 $query_args['template_type'] = $template_type;
344 }
345
346 $new_post_url = add_query_arg( $query_args, admin_url( 'edit.php' ) );
347
348 $new_post_url = add_query_arg( '_wpnonce', wp_create_nonce( 'elementor_action_new_post' ), $new_post_url );
349
350 return $new_post_url;
351 }
352
353 /**
354 * Get post autosave.
355 *
356 * Retrieve an autosave for any given post.
357 *
358 * @since 1.9.2
359 * @access public
360 * @static
361 *
362 * @param int $post_id Post ID.
363 * @param int $user_id Optional. User ID. Default is `0`.
364 *
365 * @return \WP_Post|false Post autosave or false.
366 */
367 public static function get_post_autosave( $post_id, $user_id = 0 ) {
368 global $wpdb;
369
370 $post = get_post( $post_id );
371
372 $where = $wpdb->prepare( 'post_parent = %d AND post_name LIKE %s AND post_modified_gmt > %s', [ $post_id, "{$post_id}-autosave%", $post->post_modified_gmt ] );
373
374 if ( $user_id ) {
375 $where .= $wpdb->prepare( ' AND post_author = %d', $user_id );
376 }
377
378 $revision = $wpdb->get_row( "SELECT * FROM $wpdb->posts WHERE $where AND post_type = 'revision'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
379
380 if ( $revision ) {
381 $revision = new \WP_Post( $revision );
382 } else {
383 $revision = false;
384 }
385
386 return $revision;
387 }
388
389 /**
390 * Is CPT supports custom templates.
391 *
392 * Whether the Custom Post Type supports templates.
393 *
394 * @since 2.0.0
395 * @access public
396 * @static
397 *
398 * @return bool True is templates are supported, False otherwise.
399 */
400 public static function is_cpt_custom_templates_supported() {
401 require_once ABSPATH . '/wp-admin/includes/theme.php';
402
403 return method_exists( wp_get_theme(), 'get_post_templates' );
404 }
405
406 /**
407 * @since 2.1.2
408 * @access public
409 * @static
410 */
411 public static function array_inject( $array, $key, $insert ) {
412 $length = array_search( $key, array_keys( $array ), true ) + 1;
413
414 return array_slice( $array, 0, $length, true ) +
415 $insert +
416 array_slice( $array, $length, null, true );
417 }
418
419 /**
420 * Render html attributes
421 *
422 * @access public
423 * @static
424 * @param array $attributes
425 *
426 * @return string
427 */
428 public static function render_html_attributes( array $attributes ) {
429 $rendered_attributes = [];
430
431 foreach ( $attributes as $attribute_key => $attribute_values ) {
432 if ( is_array( $attribute_values ) ) {
433 $attribute_values = implode( ' ', $attribute_values );
434 }
435
436 $rendered_attributes[] = sprintf( '%1$s="%2$s"', $attribute_key, esc_attr( $attribute_values ) );
437 }
438
439 return implode( ' ', $rendered_attributes );
440 }
441
442 public static function get_meta_viewport( $context = '' ) {
443 $meta_tag = '<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />';
444 /**
445 * Viewport meta tag.
446 *
447 * Filters the Elementor preview URL.
448 *
449 * @since 2.5.0
450 *
451 * @param string $meta_tag Viewport meta tag.
452 */
453 return apply_filters( 'elementor/template/viewport_tag', $meta_tag, $context );
454 }
455
456 /**
457 * Add Elementor Config js vars to the relevant script handle,
458 * WP will wrap it with <script> tag.
459 * To make sure this script runs thru the `script_loader_tag` hook, use a known handle value.
460 * @param string $handle
461 * @param string $js_var
462 * @param mixed $config
463 */
464 public static function print_js_config( $handle, $js_var, $config ) {
465 $config = wp_json_encode( $config );
466
467 if ( get_option( 'elementor_editor_break_lines' ) ) {
468 // Add new lines to avoid memory limits in some hosting servers that handles the buffer output according to new line characters
469 $config = str_replace( '}},"', '}},' . PHP_EOL . '"', $config );
470 }
471
472 $script_data = 'var ' . $js_var . ' = ' . $config . ';';
473
474 wp_add_inline_script( $handle, $script_data, 'before' );
475 }
476
477 public static function handle_deprecation( $item, $version, $replacement = null ) {
478 preg_match( '/^[0-9]+\.[0-9]+/', ELEMENTOR_VERSION, $current_version );
479
480 $current_version_as_float = (float) $current_version[0];
481
482 preg_match( '/^[0-9]+\.[0-9]+/', $version, $alias_version );
483
484 $alias_version_as_float = (float) $alias_version[0];
485
486 if ( round( $current_version_as_float - $alias_version_as_float, 1 ) >= self::DEPRECATION_RANGE ) {
487 _deprecated_file( $item, $version, $replacement );
488 }
489 }
490
491 /**
492 * Checks a control value for being empty, including a string of '0' not covered by PHP's empty().
493 *
494 * @param mixed $source
495 * @param bool|string $key
496 *
497 * @return bool
498 */
499 public static function is_empty( $source, $key = false ) {
500 if ( is_array( $source ) ) {
501 if ( ! isset( $source[ $key ] ) ) {
502 return true;
503 }
504
505 $source = $source[ $key ];
506 }
507
508 return '0' !== $source && empty( $source );
509 }
510
511 public static function has_pro() {
512 return defined( 'ELEMENTOR_PRO_VERSION' );
513 }
514
515 /**
516 * Convert HTMLEntities to UTF-8 characters
517 *
518 * @param $string
519 * @return string
520 */
521 public static function urlencode_html_entities( $string ) {
522 $entities_dictionary = [
523 '&#145;' => "'", // Opening single quote
524 '&#146;' => "'", // Closing single quote
525 '&#147;' => '"', // Closing double quote
526 '&#148;' => '"', // Opening double quote
527 '&#8216;' => "'", // Closing single quote
528 '&#8217;' => "'", // Opening single quote
529 '&#8218;' => "'", // Single low quote
530 '&#8220;' => '"', // Closing double quote
531 '&#8221;' => '"', // Opening double quote
532 '&#8222;' => '"', // Double low quote
533 ];
534
535 // Decode decimal entities
536 $string = str_replace( array_keys( $entities_dictionary ), array_values( $entities_dictionary ), $string );
537
538 return rawurlencode( html_entity_decode( $string, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
539 }
540
541 /**
542 * Parse attributes that come as a string of comma-delimited key|value pairs.
543 * Removes Javascript events and unescaped `href` attributes.
544 *
545 * @param string $attributes_string
546 *
547 * @param string $delimiter Default comma `,`.
548 *
549 * @return array
550 */
551 public static function parse_custom_attributes( $attributes_string, $delimiter = ',' ) {
552 $attributes = explode( $delimiter, $attributes_string );
553 $result = [];
554
555 foreach ( $attributes as $attribute ) {
556 $attr_key_value = explode( '|', $attribute );
557
558 $attr_key = mb_strtolower( $attr_key_value[0] );
559
560 // Remove any not allowed characters.
561 preg_match( '/[-_a-z0-9]+/', $attr_key, $attr_key_matches );
562
563 if ( empty( $attr_key_matches[0] ) ) {
564 continue;
565 }
566
567 $attr_key = $attr_key_matches[0];
568
569 // Avoid Javascript events and unescaped href.
570 if ( 'href' === $attr_key || 'on' === substr( $attr_key, 0, 2 ) ) {
571 continue;
572 }
573
574 if ( isset( $attr_key_value[1] ) ) {
575 $attr_value = trim( $attr_key_value[1] );
576 } else {
577 $attr_value = '';
578 }
579
580 $result[ $attr_key ] = $attr_value;
581 }
582
583 return $result;
584 }
585
586 public static function find_element_recursive( $elements, $id ) {
587 foreach ( $elements as $element ) {
588 if ( $id === $element['id'] ) {
589 return $element;
590 }
591
592 if ( ! empty( $element['elements'] ) ) {
593 $element = self::find_element_recursive( $element['elements'], $id );
594
595 if ( $element ) {
596 return $element;
597 }
598 }
599 }
600
601 return false;
602 }
603
604 /**
605 * Change Submenu First Item Label
606 *
607 * Overwrite the label of the first submenu item of an admin menu item.
608 *
609 * Fired by `admin_menu` action.
610 *
611 * @since 3.1.0
612 *
613 * @param $menu_slug
614 * @param $new_label
615 * @access public
616 */
617 public static function change_submenu_first_item_label( $menu_slug, $new_label ) {
618 global $submenu;
619
620 if ( isset( $submenu[ $menu_slug ] ) ) {
621 // @codingStandardsIgnoreStart
622 $submenu[ $menu_slug ][0][0] = $new_label;
623 // @codingStandardsIgnoreEnd
624 }
625 }
626 }
627