tracker.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <?php
  2. namespace Elementor;
  3. if ( ! defined( 'ABSPATH' ) ) {
  4. exit; // Exit if accessed directly.
  5. }
  6. /**
  7. * Elementor tracker.
  8. *
  9. * Elementor tracker handler class is responsible for sending anonymous plugin
  10. * data to Elementor servers for users that actively allowed data tracking.
  11. *
  12. * @since 1.0.0
  13. */
  14. class Tracker {
  15. /**
  16. * API URL.
  17. *
  18. * Holds the URL of the Tracker API.
  19. *
  20. * @since 1.0.0
  21. * @access private
  22. *
  23. * @var string API URL.
  24. */
  25. private static $_api_url = 'http://my.elementor.com/api/v1/tracker/';
  26. private static $notice_shown = false;
  27. /**
  28. * Init.
  29. *
  30. * Initialize Elementor tracker.
  31. *
  32. * @since 1.0.0
  33. * @access public
  34. * @static
  35. */
  36. public static function init() {
  37. add_action( 'elementor/tracker/send_event', [ __CLASS__, 'send_tracking_data' ] );
  38. add_action( 'admin_init', [ __CLASS__, 'handle_tracker_actions' ] );
  39. add_action( 'admin_notices', [ __CLASS__, 'admin_notices' ] );
  40. }
  41. /**
  42. * Check for settings opt-in.
  43. *
  44. * Checks whether the site admin has opted-in for data tracking, or not.
  45. *
  46. * @since 1.0.0
  47. * @access public
  48. * @static
  49. *
  50. * @param string $new_value Allowed tracking value.
  51. *
  52. * @return string Return `yes` if tracking allowed, `no` otherwise.
  53. */
  54. public static function check_for_settings_optin( $new_value ) {
  55. $old_value = get_option( 'elementor_allow_tracking', 'no' );
  56. if ( $old_value !== $new_value && 'yes' === $new_value ) {
  57. self::send_tracking_data( true );
  58. }
  59. if ( empty( $new_value ) ) {
  60. $new_value = 'no';
  61. }
  62. return $new_value;
  63. }
  64. /**
  65. * Send tracking data.
  66. *
  67. * Decide whether to send tracking data, or not.
  68. *
  69. * @since 1.0.0
  70. * @access public
  71. * @static
  72. *
  73. * @param bool $override
  74. */
  75. public static function send_tracking_data( $override = false ) {
  76. // Don't trigger this on AJAX Requests.
  77. if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
  78. return;
  79. }
  80. if ( ! self::is_allow_track() ) {
  81. return;
  82. }
  83. $last_send = self::get_last_send_time();
  84. /**
  85. * Tracker override send.
  86. *
  87. * Filters whether to override sending tracking data or not.
  88. *
  89. * @since 1.0.0
  90. *
  91. * @param bool $override Whether to override default setting or not.
  92. */
  93. $override = apply_filters( 'elementor/tracker/send_override', $override );
  94. if ( ! $override ) {
  95. $last_send_interval = strtotime( '-1 week' );
  96. /**
  97. * Tracker last send interval.
  98. *
  99. * Filters the interval of between two tracking requests.
  100. *
  101. * @since 1.0.0
  102. *
  103. * @param int $last_send_interval A date/time string. Default is `strtotime( '-1 week' )`.
  104. */
  105. $last_send_interval = apply_filters( 'elementor/tracker/last_send_interval', $last_send_interval );
  106. // Send a maximum of once per week by default.
  107. if ( $last_send && $last_send > $last_send_interval ) {
  108. return;
  109. }
  110. } else {
  111. // Make sure there is at least a 1 hour delay between override sends, we dont want duplicate calls due to double clicking links.
  112. if ( $last_send && $last_send > strtotime( '-1 hours' ) ) {
  113. return;
  114. }
  115. }
  116. // Update time first before sending to ensure it is set.
  117. update_option( 'elementor_tracker_last_send', time() );
  118. // Send here..
  119. $params = [
  120. 'system' => self::get_system_reports_data(),
  121. 'site_lang' => get_bloginfo( 'language' ),
  122. 'email' => get_option( 'admin_email' ),
  123. 'usages' => [
  124. 'posts' => self::get_posts_usage(),
  125. 'library' => self::get_library_usage(),
  126. ],
  127. 'is_first_time' => empty( $last_send ),
  128. ];
  129. /**
  130. * Tracker send tracking data params.
  131. *
  132. * Filters the data parameters when sending tracking request.
  133. *
  134. * @since 1.0.0
  135. *
  136. * @param array $params Variable to encode as JSON.
  137. */
  138. $params = apply_filters( 'elementor/tracker/send_tracking_data_params', $params );
  139. add_filter( 'https_ssl_verify', '__return_false' );
  140. wp_safe_remote_post(
  141. self::$_api_url,
  142. [
  143. 'timeout' => 25,
  144. 'blocking' => false,
  145. // 'sslverify' => false,
  146. 'body' => [
  147. 'data' => wp_json_encode( $params ),
  148. ],
  149. ]
  150. );
  151. }
  152. /**
  153. * Is allow track.
  154. *
  155. * Checks whether the site admin has opted-in for data tracking, or not.
  156. *
  157. * @since 1.0.0
  158. * @access public
  159. * @static
  160. */
  161. public static function is_allow_track() {
  162. return 'yes' === get_option( 'elementor_allow_tracking', 'no' );
  163. }
  164. /**
  165. * Handle tracker actions.
  166. *
  167. * Check if the user opted-in or opted-out and update the database.
  168. *
  169. * Fired by `admin_init` action.
  170. *
  171. * @since 1.0.0
  172. * @access public
  173. * @static
  174. */
  175. public static function handle_tracker_actions() {
  176. if ( ! isset( $_GET['elementor_tracker'] ) ) {
  177. return;
  178. }
  179. if ( 'opt_into' === $_GET['elementor_tracker'] ) {
  180. check_admin_referer( 'opt_into' );
  181. update_option( 'elementor_allow_tracking', 'yes' );
  182. self::send_tracking_data( true );
  183. }
  184. if ( 'opt_out' === $_GET['elementor_tracker'] ) {
  185. check_admin_referer( 'opt_out' );
  186. update_option( 'elementor_allow_tracking', 'no' );
  187. update_option( 'elementor_tracker_notice', '1' );
  188. }
  189. wp_redirect( remove_query_arg( 'elementor_tracker' ) );
  190. exit;
  191. }
  192. /**
  193. * Admin notices.
  194. *
  195. * Add Elementor notices to WordPress admin screen to show tracker notice.
  196. *
  197. * Fired by `admin_notices` action.
  198. *
  199. * @since 1.0.0
  200. * @access public
  201. * @static
  202. */
  203. public static function admin_notices() {
  204. // Show tracker notice after 24 hours from installed time.
  205. if ( self::get_installed_time() > strtotime( '-24 hours' ) ) {
  206. return;
  207. }
  208. if ( '1' === get_option( 'elementor_tracker_notice' ) ) {
  209. return;
  210. }
  211. if ( self::is_allow_track() ) {
  212. return;
  213. }
  214. if ( ! current_user_can( 'manage_options' ) ) {
  215. return;
  216. }
  217. $elementor_pages = new \WP_Query( [
  218. 'post_type' => 'any',
  219. 'post_status' => 'publish',
  220. 'fields' => 'ids',
  221. 'update_post_meta_cache' => false,
  222. 'update_post_term_cache' => false,
  223. 'meta_key' => '_elementor_edit_mode',
  224. 'meta_value' => 'builder',
  225. ] );
  226. if ( 2 > $elementor_pages->post_count ) {
  227. return;
  228. }
  229. self::$notice_shown = true;
  230. // TODO: Skip for development env.
  231. $optin_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_into' ), 'opt_into' );
  232. $optout_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_out' ), 'opt_out' );
  233. $tracker_description_text = __( 'Love using Elementor? Become a super contributor by opting in to our anonymous plugin data collection and to our updates. We guarantee no sensitive data is collected.', 'elementor' );
  234. /**
  235. * Tracker admin description text.
  236. *
  237. * Filters the admin notice text for anonymous data collection.
  238. *
  239. * @since 1.0.0
  240. *
  241. * @param string $tracker_description_text Description text displayed in admin notice.
  242. */
  243. $tracker_description_text = apply_filters( 'elementor/tracker/admin_description_text', $tracker_description_text );
  244. ?>
  245. <div class="notice updated elementor-message">
  246. <div class="elementor-message-inner">
  247. <div class="elementor-message-icon">
  248. <div class="e-logo-wrapper">
  249. <i class="eicon-elementor" aria-hidden="true"></i>
  250. </div>
  251. </div>
  252. <div class="elementor-message-content">
  253. <p><?php echo esc_html( $tracker_description_text ); ?> <a href="https://go.elementor.com/usage-data-tracking/" target="_blank"><?php echo __( 'Learn more.', 'elementor' ); ?></a></p>
  254. <p class="elementor-message-actions">
  255. <a href="<?php echo $optin_url; ?>" class="button button-primary"><?php echo __( 'Sure! I\'d love to help', 'elementor' ); ?></a>&nbsp;<a href="<?php echo $optout_url; ?>" class="button-secondary"><?php echo __( 'No thanks', 'elementor' ); ?></a>
  256. </p>
  257. </div>
  258. </div>
  259. </div>
  260. <?php
  261. }
  262. public static function is_notice_shown() {
  263. return self::$notice_shown;
  264. }
  265. /**
  266. * Get installed time.
  267. *
  268. * Retrieve the time when Elementor was installed.
  269. *
  270. * @since 2.0.0
  271. * @access private
  272. * @static
  273. *
  274. * @return int Unix timestamp when Elementor was installed.
  275. */
  276. private static function get_installed_time() {
  277. $installed_time = get_option( '_elementor_installed_time' );
  278. if ( ! $installed_time ) {
  279. $installed_time = time();
  280. update_option( '_elementor_installed_time', $installed_time );
  281. }
  282. return $installed_time;
  283. }
  284. /**
  285. * Get system reports data.
  286. *
  287. * Retrieve the data from system reports.
  288. *
  289. * @since 2.0.0
  290. * @access private
  291. * @static
  292. *
  293. * @return array The data from system reports.
  294. */
  295. private static function get_system_reports_data() {
  296. $reports = Plugin::$instance->system_info->load_reports( System_Info\Main::get_allowed_reports() );
  297. $system_reports = [];
  298. foreach ( $reports as $report_key => $report_details ) {
  299. $system_reports[ $report_key ] = [];
  300. foreach ( $report_details['report'] as $sub_report_key => $sub_report_details ) {
  301. $system_reports[ $report_key ][ $sub_report_key ] = $sub_report_details['value'];
  302. }
  303. }
  304. return $system_reports;
  305. }
  306. /**
  307. * Get last send time.
  308. *
  309. * Retrieve the last time tracking data was sent.
  310. *
  311. * @since 2.0.0
  312. * @access private
  313. * @static
  314. *
  315. * @return int|false The last time tracking data was sent, or false if
  316. * tracking data never sent.
  317. */
  318. private static function get_last_send_time() {
  319. $last_send_time = get_option( 'elementor_tracker_last_send', false );
  320. /**
  321. * Tracker last send time.
  322. *
  323. * Filters the last time tracking data was sent.
  324. *
  325. * @since 1.0.0
  326. *
  327. * @param int|false $last_send_time The last time tracking data was sent,
  328. * or false if tracking data never sent.
  329. */
  330. $last_send_time = apply_filters( 'elementor/tracker/last_send_time', $last_send_time );
  331. return $last_send_time;
  332. }
  333. /**
  334. * Get posts usage.
  335. *
  336. * Retrieve the number of posts using Elementor.
  337. *
  338. * @since 2.0.0
  339. * @access private
  340. * @static
  341. *
  342. * @return array The number of posts using Elementor grouped by post types
  343. * and post status.
  344. */
  345. private static function get_posts_usage() {
  346. global $wpdb;
  347. $usage = [];
  348. $results = $wpdb->get_results(
  349. "SELECT `post_type`, `post_status`, COUNT(`ID`) `hits`
  350. FROM {$wpdb->posts} `p`
  351. LEFT JOIN {$wpdb->postmeta} `pm` ON(`p`.`ID` = `pm`.`post_id`)
  352. WHERE `post_type` != 'elementor_library'
  353. AND `meta_key` = '_elementor_edit_mode' AND `meta_value` = 'builder'
  354. GROUP BY `post_type`, `post_status`;"
  355. );
  356. if ( $results ) {
  357. foreach ( $results as $result ) {
  358. $usage[ $result->post_type ][ $result->post_status ] = $result->hits;
  359. }
  360. }
  361. return $usage;
  362. }
  363. /**
  364. * Get library usage.
  365. *
  366. * Retrieve the number of Elementor library items saved.
  367. *
  368. * @since 2.0.0
  369. * @access private
  370. * @static
  371. *
  372. * @return array The number of Elementor library items grouped by post types
  373. * and meta value.
  374. */
  375. private static function get_library_usage() {
  376. global $wpdb;
  377. $usage = [];
  378. $results = $wpdb->get_results(
  379. "SELECT `meta_value`, COUNT(`ID`) `hits`
  380. FROM {$wpdb->posts} `p`
  381. LEFT JOIN {$wpdb->postmeta} `pm` ON(`p`.`ID` = `pm`.`post_id`)
  382. WHERE `post_type` = 'elementor_library'
  383. AND `meta_key` = '_elementor_template_type'
  384. GROUP BY `post_type`, `meta_value`;"
  385. );
  386. if ( $results ) {
  387. foreach ( $results as $result ) {
  388. $usage[ $result->meta_value ] = $result->hits;
  389. }
  390. }
  391. return $usage;
  392. }
  393. }