utils.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. <?php
  2. namespace Elementor;
  3. if ( ! defined( 'ABSPATH' ) ) {
  4. exit; // Exit if accessed directly.
  5. }
  6. /**
  7. * Elementor utils.
  8. *
  9. * Elementor utils handler class is responsible for different utility methods
  10. * used by Elementor.
  11. *
  12. * @since 1.0.0
  13. */
  14. class Utils {
  15. /**
  16. * Is ajax.
  17. *
  18. * Whether the current request is a WordPress ajax request.
  19. *
  20. * @since 1.0.0
  21. * @access public
  22. * @static
  23. *
  24. * @return bool True if it's a WordPress ajax request, false otherwise.
  25. */
  26. public static function is_ajax() {
  27. // TODO: When minimum required version of WordPress will be 4.7, use `wp_doing_ajax()` instead.
  28. return defined( 'DOING_AJAX' ) && DOING_AJAX;
  29. }
  30. /**
  31. * Is script debug.
  32. *
  33. * Whether script debug is enabled or not.
  34. *
  35. * @since 1.0.0
  36. * @access public
  37. * @static
  38. *
  39. * @return bool True if it's a script debug is active, false otherwise.
  40. */
  41. public static function is_script_debug() {
  42. return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
  43. }
  44. /**
  45. * Get edit link.
  46. *
  47. * Retrieve Elementor edit link.
  48. *
  49. * @since 1.0.0
  50. * @deprecated 2.0.0 Use `Plugin::$instance->documents->get( $post_id )->get_edit_url()` method instead.
  51. *
  52. * @access public
  53. * @static
  54. *
  55. * @param int $post_id Optional. Post ID. Default is `0`.
  56. *
  57. * @return string Post edit link.
  58. */
  59. public static function get_edit_link( $post_id = 0 ) {
  60. // TODO: _deprecated_function( __METHOD__, '2.0.0', 'Plugin::$instance->documents->get( $post_id )->get_edit_url()' );
  61. if ( ! $post_id ) {
  62. $post_id = get_the_ID();
  63. }
  64. $edit_link = '';
  65. $document = Plugin::$instance->documents->get( $post_id );
  66. if ( $document ) {
  67. $edit_link = $document->get_edit_url();
  68. }
  69. /**
  70. * Get edit link.
  71. *
  72. * Filters the Elementor edit link.
  73. *
  74. * @since 1.0.0
  75. * @deprecated 2.0.0 Use `elementor/document/urls/edit` filter instead.
  76. *
  77. * @param string $edit_link New URL query string (unescaped).
  78. * @param int $post_id Post ID.
  79. */
  80. $edit_link = apply_filters( 'elementor/utils/get_edit_link', $edit_link, $post_id );
  81. return $edit_link;
  82. }
  83. /**
  84. * Get pro link.
  85. *
  86. * Retrieve the link to Elementor Pro.
  87. *
  88. * @since 1.7.0
  89. * @access public
  90. * @static
  91. *
  92. * @param string $link URL to Elementor pro.
  93. *
  94. * @return string Elementor pro link.
  95. */
  96. public static function get_pro_link( $link ) {
  97. static $theme_name = false;
  98. if ( ! $theme_name ) {
  99. $theme_obj = wp_get_theme();
  100. if ( $theme_obj->parent() ) {
  101. $theme_name = $theme_obj->parent()->get( 'Name' );
  102. } else {
  103. $theme_name = $theme_obj->get( 'Name' );
  104. }
  105. $theme_name = sanitize_key( $theme_name );
  106. }
  107. $link = add_query_arg( 'utm_term', $theme_name, $link );
  108. if ( defined( 'ELEMENTOR_PARTNER_ID' ) ) {
  109. $link = add_query_arg( 'partner_id', sanitize_key( ELEMENTOR_PARTNER_ID ), $link );
  110. }
  111. return $link;
  112. }
  113. /**
  114. * Get preview URL.
  115. *
  116. * Retrieve the post preview URL.
  117. *
  118. * @since 1.6.4
  119. * @deprecated 2.0.0 Use `Plugin::$instance->documents->get( $post_id )->get_preview_url()` method instead.
  120. *
  121. * @access public
  122. * @static
  123. *
  124. * @param int $post_id Optional. Post ID. Default is `0`.
  125. *
  126. * @return string Post preview URL.
  127. */
  128. public static function get_preview_url( $post_id ) {
  129. // TODO: _deprecated_function( __METHOD__, '2.0.0', 'Plugin::$instance->documents->get( $post_id )->get_preview_url()' );
  130. $url = Plugin::$instance->documents->get( $post_id )->get_preview_url();
  131. /**
  132. * Preview URL.
  133. *
  134. * Filters the Elementor preview URL.
  135. *
  136. * @since 1.6.4
  137. * @deprecated 2.0.0 Use `elementor/document/urls/preview` filter instead.
  138. *
  139. * @param string $preview_url URL with chosen scheme.
  140. * @param int $post_id Post ID.
  141. */
  142. $url = apply_filters( 'elementor/utils/preview_url', $url, $post_id );
  143. return $url;
  144. }
  145. /**
  146. * Get WordPress preview url.
  147. *
  148. * Retrieve WordPress preview URL for any given post ID.
  149. *
  150. * @since 1.9.0
  151. * @deprecated 2.0.0 Use `Plugin::$instance->documents->get( $post_id )->get_wp_preview_url()` method instead.
  152. *
  153. * @access public
  154. * @static
  155. *
  156. * @param int $post_id Post ID.
  157. *
  158. * @return string WordPress preview URL.
  159. */
  160. public static function get_wp_preview_url( $post_id ) {
  161. // TODO: _deprecated_function( __METHOD__, '2.0.0', 'Plugin::$instance->documents->get( $post_id )->get_wp_preview_url()' );
  162. $wp_preview_url = Plugin::$instance->documents->get( $post_id )->get_wp_preview_url();
  163. /**
  164. * WordPress preview URL.
  165. *
  166. * Filters the WordPress preview URL.
  167. *
  168. * @since 1.9.0
  169. * @deprecated 2.0.0 Use `elementor/document/urls/wp_preview` filter instead.
  170. *
  171. * @param string $wp_preview_url WordPress preview URL.
  172. * @param int $post_id Post ID.
  173. */
  174. $wp_preview_url = apply_filters( 'elementor/utils/wp_preview_url', $wp_preview_url, $post_id );
  175. return $wp_preview_url;
  176. }
  177. /**
  178. * Replace URLs.
  179. *
  180. * Replace old URLs to new URLs. This method also updates all the Elementor data.
  181. *
  182. * @since 2.1.0
  183. * @access public
  184. *
  185. * @param $from
  186. * @param $to
  187. *
  188. * @return string
  189. * @throws \Exception
  190. */
  191. public static function replace_urls( $from, $to ) {
  192. $from = trim( $from );
  193. $to = trim( $to );
  194. if ( $from === $to ) {
  195. throw new \Exception( __( 'The `from` and `to` URL\'s must be different', 'elementor' ) );
  196. }
  197. $is_valid_urls = ( filter_var( $from, FILTER_VALIDATE_URL ) && filter_var( $to, FILTER_VALIDATE_URL ) );
  198. if ( ! $is_valid_urls ) {
  199. throw new \Exception( __( 'The `from` and `to` URL\'s must be valid URL\'s', 'elementor' ) );
  200. }
  201. global $wpdb;
  202. // @codingStandardsIgnoreStart cannot use `$wpdb->prepare` because it remove's the backslashes
  203. $rows_affected = $wpdb->query(
  204. "UPDATE {$wpdb->postmeta} " .
  205. "SET `meta_value` = REPLACE(`meta_value`, '" . str_replace( '/', '\\\/', $from ) . "', '" . str_replace( '/', '\\\/', $to ) . "') " .
  206. "WHERE `meta_key` = '_elementor_data' AND `meta_value` LIKE '[%' ;" ); // meta_value LIKE '[%' are json formatted
  207. // @codingStandardsIgnoreEnd
  208. if ( false === $rows_affected ) {
  209. throw new \Exception( __( 'An error occurred', 'elementor' ) );
  210. }
  211. Plugin::$instance->files_manager->clear_cache();
  212. return sprintf(
  213. /* translators: %d: Number of rows */
  214. _n( '%d row affected.', '%d rows affected.', $rows_affected, 'elementor' ),
  215. $rows_affected
  216. );
  217. }
  218. /**
  219. * Get exit to dashboard URL.
  220. *
  221. * Retrieve WordPress preview URL for any given post ID.
  222. *
  223. * @since 1.9.0
  224. * @deprecated 2.0.0 Use `Plugin::$instance->documents->get( $post_id )->get_exit_to_dashboard_url()` method instead.
  225. *
  226. * @access public
  227. * @static
  228. *
  229. * @param int $post_id Post ID.
  230. *
  231. * @return string Exit to dashboard URL.
  232. */
  233. public static function get_exit_to_dashboard_url( $post_id ) {
  234. // TODO: _deprecated_function( __METHOD__, '2.0.0', 'Plugin::$instance->documents->get( $post_id )->get_exit_to_dashboard_url()' );
  235. return Plugin::$instance->documents->get( $post_id )->get_exit_to_dashboard_url();
  236. }
  237. /**
  238. * Is post supports Elementor.
  239. *
  240. * Whether the post supports editing with Elementor.
  241. *
  242. * @since 1.0.0
  243. * @access public
  244. * @static
  245. *
  246. * @param int $post_id Optional. Post ID. Default is `0`.
  247. *
  248. * @return string True if post supports editing with Elementor, false otherwise.
  249. */
  250. public static function is_post_support( $post_id = 0 ) {
  251. $post_type = get_post_type( $post_id );
  252. $is_supported = self::is_post_type_support( $post_type );
  253. /**
  254. * Is post type support.
  255. *
  256. * Filters whether the post type supports editing with Elementor.
  257. *
  258. * @since 1.0.0
  259. * @deprecated 2.2.0 Use `elementor/utils/is_post_support` Instead
  260. *
  261. * @param bool $is_supported Whether the post type supports editing with Elementor.
  262. * @param int $post_id Post ID.
  263. * @param string $post_type Post type.
  264. */
  265. $is_supported = apply_filters( 'elementor/utils/is_post_type_support', $is_supported, $post_id, $post_type );
  266. /**
  267. * Is post support.
  268. *
  269. * Filters whether the post supports editing with Elementor.
  270. *
  271. * @since 2.2.0
  272. *
  273. * @param bool $is_supported Whether the post type supports editing with Elementor.
  274. * @param int $post_id Post ID.
  275. * @param string $post_type Post type.
  276. */
  277. $is_supported = apply_filters( 'elementor/utils/is_post_support', $is_supported, $post_id, $post_type );
  278. return $is_supported;
  279. }
  280. /**
  281. * Is post type supports Elementor.
  282. *
  283. * Whether the post type supports editing with Elementor.
  284. *
  285. * @since 2.2.0
  286. * @access public
  287. * @static
  288. *
  289. * @param string $post_type Post Type.
  290. *
  291. * @return string True if post type supports editing with Elementor, false otherwise.
  292. */
  293. public static function is_post_type_support( $post_type ) {
  294. if ( ! post_type_exists( $post_type ) ) {
  295. return false;
  296. }
  297. if ( ! post_type_supports( $post_type, 'elementor' ) ) {
  298. return false;
  299. }
  300. return true;
  301. }
  302. /**
  303. * Get placeholder image source.
  304. *
  305. * Retrieve the source of the placeholder image.
  306. *
  307. * @since 1.0.0
  308. * @access public
  309. * @static
  310. *
  311. * @return string The source of the default placeholder image used by Elementor.
  312. */
  313. public static function get_placeholder_image_src() {
  314. $placeholder_image = ELEMENTOR_ASSETS_URL . 'images/placeholder.png';
  315. /**
  316. * Get placeholder image source.
  317. *
  318. * Filters the source of the default placeholder image used by Elementor.
  319. *
  320. * @since 1.0.0
  321. *
  322. * @param string $placeholder_image The source of the default placeholder image.
  323. */
  324. $placeholder_image = apply_filters( 'elementor/utils/get_placeholder_image_src', $placeholder_image );
  325. return $placeholder_image;
  326. }
  327. /**
  328. * Generate random string.
  329. *
  330. * Returns a string containing a hexadecimal representation of random number.
  331. *
  332. * @since 1.0.0
  333. * @access public
  334. * @static
  335. *
  336. * @return string Random string.
  337. */
  338. public static function generate_random_string() {
  339. return dechex( rand() );
  340. }
  341. /**
  342. * Do not cache.
  343. *
  344. * Tell WordPress cache plugins not to cache this request.
  345. *
  346. * @since 1.0.0
  347. * @access public
  348. * @static
  349. */
  350. public static function do_not_cache() {
  351. if ( ! defined( 'DONOTCACHEPAGE' ) ) {
  352. define( 'DONOTCACHEPAGE', true );
  353. }
  354. if ( ! defined( 'DONOTCACHEDB' ) ) {
  355. define( 'DONOTCACHEDB', true );
  356. }
  357. if ( ! defined( 'DONOTMINIFY' ) ) {
  358. define( 'DONOTMINIFY', true );
  359. }
  360. if ( ! defined( 'DONOTCDN' ) ) {
  361. define( 'DONOTCDN', true );
  362. }
  363. if ( ! defined( 'DONOTCACHCEOBJECT' ) ) {
  364. define( 'DONOTCACHCEOBJECT', true );
  365. }
  366. // Set the headers to prevent caching for the different browsers.
  367. nocache_headers();
  368. }
  369. /**
  370. * Get timezone string.
  371. *
  372. * Retrieve timezone string from the WordPress database.
  373. *
  374. * @since 1.0.0
  375. * @access public
  376. * @static
  377. *
  378. * @return string Timezone string.
  379. */
  380. public static function get_timezone_string() {
  381. $current_offset = (float) get_option( 'gmt_offset' );
  382. $timezone_string = get_option( 'timezone_string' );
  383. // Create a UTC+- zone if no timezone string exists.
  384. if ( empty( $timezone_string ) ) {
  385. if ( $current_offset < 0 ) {
  386. $timezone_string = 'UTC' . $current_offset;
  387. } else {
  388. $timezone_string = 'UTC+' . $current_offset;
  389. }
  390. }
  391. return $timezone_string;
  392. }
  393. /**
  394. * Do action deprecated.
  395. *
  396. * Fires functions attached to a deprecated action hook.
  397. *
  398. * @since 1.0.10
  399. * @access public
  400. * @static
  401. * @deprecated 2.1.0 Use `do_action_deprecated()` instead
  402. *
  403. * @param string $tag The name of the action hook.
  404. * @param array $args Array of additional function arguments to be passed to `do_action()`.
  405. * @param string $version The version of WordPress that deprecated the hook.
  406. * @param bool $replacement Optional. The hook that should have been used.
  407. * @param string $message Optional. A message regarding the change.
  408. */
  409. public static function do_action_deprecated( $tag, $args, $version, $replacement = false, $message = null ) {
  410. _deprecated_function( __METHOD__, '2.1.0', 'do_action_deprecated()' );
  411. do_action_deprecated( $tag, $args, $version, $replacement, $message );
  412. }
  413. /**
  414. * Do filter deprecated.
  415. *
  416. * Fires functions attached to a deprecated filter hook.
  417. *
  418. * @since 1.0.10
  419. * @access public
  420. * @static
  421. * @deprecated 2.1.0 Use `apply_filters_deprecated()` instead
  422. *
  423. * @param string $tag The name of the filter hook.
  424. * @param array $args Array of additional function arguments to be passed to `apply_filters()`.
  425. * @param string $version The version of WordPress that deprecated the hook.
  426. * @param bool $replacement Optional. The hook that should have been used.
  427. * @param string $message Optional. A message regarding the change.
  428. *
  429. * @return mixed The filtered value after all hooked functions are applied to it.
  430. */
  431. public static function apply_filters_deprecated( $tag, $args, $version, $replacement = false, $message = null ) {
  432. _deprecated_function( __METHOD__, '2.1.0', 'apply_filters_deprecated()' );
  433. return apply_filters_deprecated( $tag, $args, $version, $replacement, $message );
  434. }
  435. /**
  436. * Get last edited string.
  437. *
  438. * Retrieve a string saying when the post was saved or the last time it was edited.
  439. *
  440. * @since 1.9.0
  441. * @deprecated 2.0.0 Use `Plugin::$instance->documents->get()` method instead.
  442. *
  443. * @access public
  444. * @static
  445. *
  446. * @param int $post_id Post ID.
  447. *
  448. * @return string Last edited string.
  449. */
  450. public static function get_last_edited( $post_id ) {
  451. // TODO: _deprecated_function( __METHOD__, '2.0.0', 'Plugin::$instance->documents->get()' );
  452. $document = Plugin::$instance->documents->get( $post_id );
  453. return $document->get_last_edited();
  454. }
  455. /**
  456. * Get create new post URL.
  457. *
  458. * Retrieve a custom URL for creating a new post/page using Elementor.
  459. *
  460. * @since 1.9.0
  461. * @access public
  462. * @static
  463. *
  464. * @param string $post_type Optional. Post type slug. Default is 'page'.
  465. *
  466. * @return string A URL for creating new post using Elementor.
  467. */
  468. public static function get_create_new_post_url( $post_type = 'page' ) {
  469. $new_post_url = add_query_arg( [
  470. 'action' => 'elementor_new_post',
  471. 'post_type' => $post_type,
  472. ], admin_url( 'edit.php' ) );
  473. $new_post_url = wp_nonce_url( $new_post_url, 'elementor_action_new_post' );
  474. return $new_post_url;
  475. }
  476. /**
  477. * Get post autosave.
  478. *
  479. * Retrieve an autosave for any given post.
  480. *
  481. * @since 1.9.2
  482. * @access public
  483. * @static
  484. *
  485. * @param int $post_id Post ID.
  486. * @param int $user_id Optional. User ID. Default is `0`.
  487. *
  488. * @return \WP_Post|false Post autosave or false.
  489. */
  490. public static function get_post_autosave( $post_id, $user_id = 0 ) {
  491. global $wpdb;
  492. $post = get_post( $post_id );
  493. $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 ] );
  494. if ( $user_id ) {
  495. $where .= $wpdb->prepare( ' AND post_author = %d', $user_id );
  496. }
  497. $revision = $wpdb->get_row( "SELECT * FROM $wpdb->posts WHERE $where AND post_type = 'revision'" ); // WPCS: unprepared SQL ok.
  498. if ( $revision ) {
  499. $revision = new \WP_Post( $revision );
  500. } else {
  501. $revision = false;
  502. }
  503. return $revision;
  504. }
  505. /**
  506. * Is CPT supports custom templates.
  507. *
  508. * Whether the Custom Post Type supports templates.
  509. *
  510. * @since 2.0.0
  511. * @access public
  512. * @static
  513. *
  514. * @return bool True is templates are supported, False otherwise.
  515. */
  516. public static function is_cpt_custom_templates_supported() {
  517. require_once ABSPATH . '/wp-admin/includes/theme.php';
  518. return method_exists( wp_get_theme(), 'get_post_templates' );
  519. }
  520. public static function array_inject( $array, $key, $insert ) {
  521. $length = array_search( $key, array_keys( $array ), true ) + 1;
  522. return array_slice( $array, 0, $length, true ) +
  523. $insert +
  524. array_slice( $array, $length, null, true );
  525. }
  526. }