PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.8.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.8.0
2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.2.0 1.2.1 All 78 releases
ablocks / includes / admin / insights.php

insights.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.8.0, at includes/admin/insights.php

505 lines 17.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace ABlocks\Admin;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 // phpcs:ignoreFile
9
10 class Insights {
11 private static $instances = [];
12
13 private $slug;
14 private $type;
15 private $version;
16 private $api_site_url;
17 private $config = [];
18
19 private function __construct( $api_site, $slug, $type = 'plugin', $version = null, $config = [] ) {
20 $this->api_site_url = untrailingslashit( $api_site );
21 $this->slug = sanitize_title( $slug );
22 $this->type = $type === 'theme' ? 'theme' : 'plugin';
23 $this->version = $version;
24 $default_config = [
25 'logo' => '', // default logo URL
26 'optin_message' => 'Help improve ' . esc_html( $this->slug ) . '! Allow anonymous usage tracking?',
27 'deactivation_message' => 'If you have a moment, please share why you are deactivating:',
28 'deactivation_reasons' => [
29 'no_longer_needed' => [
30 'label' => 'I no longer need the plugin',
31 ],
32 'found_a_better_plugin' => [
33 'label' => 'I found a better plugin',
34 'has_custom_reason' => true,
35 'custom_reason_placeholder' => 'Please share which plugin',
36 ],
37 'couldnt_get_the_plugin_to_work' => [
38 'label' => 'I couldn\'t get the plugin to work',
39 ],
40 'temporary_deactivation' => [
41 'label' => 'It\'s a temporary deactivation',
42 ],
43 'other' => [
44 'label' => 'Other',
45 'has_custom_reason' => true,
46 'custom_reason_placeholder' => 'Please share the reason',
47 ],
48 ],
49 ];
50
51 $this->config = wp_parse_args( $config, $default_config );
52
53 add_action( 'admin_notices', [ $this, 'maybe_show_optin' ] );
54 add_action( 'admin_init', [ $this, 'handle_optin_click' ] );
55 add_action( 'admin_enqueue_scripts', [ $this, 'inject_deactivate_feedback' ] );
56 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_inline_css' ] );
57
58 add_action( 'wp_ajax_insights_deactivate_send', function () {
59 if ( ! wp_verify_nonce( sanitize_text_field( $_POST['security'] ), $this->slug . '-insights-deactivate-nonce' ) ) {
60 wp_send_json_error( 'Invalid nonce.' );
61 }
62
63 $slug = sanitize_text_field( $_POST['slug'] );
64 $reason_key = sanitize_text_field( $_POST['reason_key'] );
65 $reason = sanitize_text_field( $_POST['reason'] );
66 $instance = self::$instances[ $slug ] ?? null;
67
68 if ( ! $instance || empty( $slug ) ) {
69 wp_send_json_error();
70 }
71
72 $instance->send_to_server( 'deactivated', [
73 'deactivation_reason_type' => $reason_key,
74 'deactivation_reason' => $reason,
75 'deactivation_time' => current_time( 'mysql' ),
76 ] );
77 $instance->mark_sent( 'deactivated' );
78
79 wp_send_json_success();
80 } );
81 add_action( 'wp_ajax_insights_optin', function () {
82 if ( ! wp_verify_nonce( sanitize_text_field( $_POST['security'] ), $this->slug . '_nonce' ) ) {
83 wp_send_json_error( 'Invalid nonce.' );
84 }
85
86 $slug = sanitize_text_field( $_POST['slug'] );
87 $instance = self::$instances[ $slug ] ?? null;
88
89 if ( ! $instance || empty( $slug ) ) {
90 wp_send_json_error();
91 }
92
93 $instance->send_to_server( 'optin', [
94 'optin_status' => true,
95 'optin_time' => current_time( 'mysql' ),
96 ] );
97 $instance->mark_sent( 'optin' );
98
99 wp_send_json_success();
100 } );
101 }
102
103 public static function init( $api_site, $slug, $type = 'plugin', $version = null, $config = [] ) {
104 if ( ! isset( self::$instances[ $slug ] ) ) {
105 self::$instances[ $slug ] = new self( $api_site, $slug, $type, $version, $config );
106 }
107 }
108
109 public function maybe_show_optin() {
110 $screen = get_current_screen();
111 if ( $screen && (in_array( $screen->base, [ 'post', 'post-new' ], true ) || $screen->is_block_editor()) ) {
112 return;
113 }
114
115 if ( $this->is_already_sent( 'optin' ) ) {
116 return;
117 }
118
119 $install_time = get_option('ablocks_first_install_time');
120 $current_time = \ABlocks\Helper::get_time();
121 $three_days = 3 * DAY_IN_SECONDS;
122
123 if ( ! $install_time || ! is_numeric( $install_time ) ) {
124 return;
125 }
126
127 if ( ( $current_time - $install_time ) < $three_days ) {
128 return;
129 }
130
131 $optin_key = 'insights_optin_' . $this->slug;
132 $nonce = wp_create_nonce( 'insights_optin_' . $this->slug );
133 $optin_url = esc_url( add_query_arg( [ $optin_key => 'yes', 'security' => $nonce ] ) );
134 $nooptin_url = esc_url( add_query_arg( [ $optin_key => 'no', 'security' => $nonce ] ) );
135
136 echo '<div class="notice notice-info" style="padding:0;border-top:0;border-right:0;border-bottom:0;margin:1rem; display: flex; border-left-color: #13191B;">
137 <div style="padding: 24px; background: #F4F4F5; display:flex; flex-direction: column; justify-content:center; align-item: center;">
138 <img src="' . esc_url( ABLOCKS_ASSETS_URL . 'images/logo.svg' ) . '" width="100" alt="logo" style="margin-bottom: 20px;" />
139 <p>Connect with over 2,000+ <br/>Web Design professionals.</p>
140 <a href="https://www.facebook.com/groups/386840187678401" target="_blank" style="color: #13191B;">Join the community</a>
141 </div>
142 <div style="padding: 24px;">
143 <h2 style="margin-top: 0; font-size: 20x; font-weight:bold; color: #1E1E1E;"> 💌 Help Us Improve & Get Exclusive Perks!</h2>
144 <p>
145 We’d love to stay in touch and share helpful updates, tips, and special offers to make the most of <strong>aBlocks</strong>. Your privacy is our priority. No spam — ever.
146 </p>
147 <ul>
148 <li>�
149 Early access to new features</li>
150 <li>�
151 Helpful guides & tutorials</li>
152 <li>�
153 Occasional discounts</li>
154 </ul>
155 <details style="margin: 10px 0;">
156 <summary style="cursor: pointer; color: #13191B; text-decoration: underline;">What we collect if you opt in?</summary>
157 <div style="margin-top: 8px;">
158 <p>We collect your <strong>site URL, plugin version, WordPress version, PHP version</strong>, and <strong>admin email</strong> to better tailor our updates and improve plugin performance.</p>
159 <p><em>No sensitive or personal data is collected. You can unsubscribe at any time.</em></p>
160 </div>
161 </details>
162 <p style="display: flex;column-gap: 15px; margin-bottom: 0;">
163 <a href="' . esc_url( $optin_url ) . '" class="button-primary" style="background: #13191B; border-color: #13191B;">Yes, keep me updated</a>
164 <a href="' . esc_url( $nooptin_url ) . '" class="button-secondary" style="background: #F4F4F5; color: #13191B; border-color: #F4F4F5;">No thanks</a>
165 </p>
166 </div>
167 </div>';
168 }
169
170 public function handle_optin_click() {
171 $key = 'insights_optin_' . $this->slug;
172 if ( isset( $_GET[ $key ] ) && wp_verify_nonce( sanitize_text_field( $_GET['security'] ), 'insights_optin_' . $this->slug ) ) {
173 $optin = $_GET[ $key ] === 'yes' ? 1 : 0;
174 if($optin){
175 $this->send_to_server( 'optin', [
176 'optin_status' => (int) $optin,
177 'optin_time' => current_time( 'mysql' ),
178 ] );
179 }
180 $this->mark_sent( 'optin' );
181 wp_safe_redirect( remove_query_arg( $key ) );
182 exit;
183 }
184 }
185
186 public function inject_deactivate_feedback( $hook ) {
187 if ( $hook !== 'plugins.php' || $this->is_already_sent( 'deactivated' ) ) {
188 return;
189 }
190
191 wp_enqueue_script( 'jquery-ui-dialog' );
192 wp_enqueue_style( 'jquery-ui-dialog' );
193
194 add_action( 'admin_footer', function () {
195 $slug = esc_js( $this->slug );
196 $ajax_url = esc_url( admin_url( 'admin-ajax.php?action=insights_deactivate_send' ) );
197 ?>
198 <script>
199 jQuery(function ($) {
200 const $link = $('tr[data-slug="<?php echo esc_attr( $slug ); ?>"] .deactivate a');
201 const dialogId = 'insights-deactivate-dialog-<?php echo esc_attr( $slug ); ?>';
202 const formId = 'insights-deactivate-form-<?php echo esc_attr( $slug ); ?>';
203
204 const $dialog = $(`
205 <div id="${dialogId}">
206 <div class="insights-deactivate-dialog__header">
207 <img src="<?php echo esc_url( $this->config['logo'] ); ?>" width="20" />
208 <span>Quick Feedback</span>
209 </div>
210 <form id="${formId}" class="insights-deactivate-form">
211 <input type="hidden" name="security" value="<?php echo esc_attr( wp_create_nonce( $this->slug . '-insights-deactivate-nonce' ) ); ?>" />
212 <input type="hidden" name="slug" value="<?php echo esc_attr( $slug ); ?>" />
213 <div class="insights-deactivate-dialog__description"><?php echo esc_html( $this->config['deactivation_message'] ); ?></div>
214 <ul>
215 <?php foreach ( $this->config['deactivation_reasons'] as $reason_key => $reason ) : ?>
216 <li>
217 <label style="display:block; margin-bottom: 8px;">
218 <input type="radio" name="reason_key" value="<?php echo esc_attr( $reason_key ); ?>" required />
219 <?php echo esc_html( $reason['label'] ); ?>
220 </label>
221 <?php
222 if ( isset( $reason['toggle_text'] ) && ! empty( $reason['toggle_text'] ) ) :
223 ?>
224 <div class="insights-toggle-text" style="color: #F59E0B; display: none; margin-left: 25px;">
225 <?php echo esc_html( $reason['toggle_text'] ); ?>
226 <div>
227 <?php
228 endif;
229 ?>
230
231 <?php
232 if ( isset( $reason['has_custom_reason'] ) && $reason['has_custom_reason'] === true ) :
233 ?>
234 <div class="insights-deactivate-form-reason-message">
235 <input type="text" name="reason" placeholder="<?php echo esc_attr( $reason['custom_reason_placeholder'] ); ?>" required />
236 <div>
237 <?php
238 endif;
239 ?>
240 </li>
241 <?php endforeach; ?>
242 </ul>
243 <div class="insights-deactivate-form__control-button">
244 <button type="submit">Submit & Deactivate</button>
245 <button type="button" data-type="skip">Skip & Deactivate</button>
246 </div>
247 </form>
248 </div>
249 `).appendTo('body').hide();
250
251 $link.on('click', function (e) {
252 e.preventDefault();
253 const href = $(this).attr('href');
254
255 $(`#${dialogId}`).dialog({
256 modal: true,
257 width: 550,
258 dialogClass: 'insights-deactivate-dialog',
259 buttons: [],
260 create: function () {
261 const $thisDialog = $(this);
262 // Hide the default close (X) button
263 $thisDialog.closest('.ui-dialog').find('.ui-dialog-titlebar-close').hide();
264 },
265 close: function () {
266 // Clean up delegated handler
267 $(document).off('click.insightsOverlay');
268 }
269
270 });
271
272 const $form = $(`#${formId}`);
273
274 // Unbind first to prevent multiple bindings
275 $form.off('submit').on('submit', function (event) {
276 event.preventDefault();
277
278 const $submitButton = $(this).find('button[type="submit"]');
279 const originalHtml = $submitButton.html();
280
281 // Add WP spinner
282 $submitButton.prop('disabled', true).html('Deactivating...');
283
284 const formDataArray = $form.serializeArray();
285 const formDataObject = {};
286
287 formDataArray.forEach(field => {
288 if (field.value.trim() !== '') {
289 formDataObject[field.name] = field.value;
290 }
291 });
292
293 $.ajax({
294 url: '<?php echo $ajax_url; ?>',
295 method: 'POST',
296 data: formDataObject,
297 success: function (response) {
298 window.location.href = href;
299 },
300 error: function () {
301 alert('An error occurred. Please try again.');
302 $submitButton.prop('disabled', false).html(originalHtml);
303 }
304 });
305 });
306
307
308 // Skip button closes modal and redirects
309 $form.find('button[data-type="skip"]').off('click').on('click', function () {
310 $(`#${dialogId}`).dialog('close');
311 window.location.href = href;
312 });
313
314 $(document).on('click.insightsOverlay', '.ui-widget-overlay', function () {
315 // Get the visible dialog that is open now
316 const $openDialog = $('.ui-dialog-content:visible');
317 if ($openDialog.length) {
318 $openDialog.dialog('close');
319 }
320 });
321
322 $form.find('input[name="reason_key"]').on('change', function () {
323 const $selected = $(this).closest('li');
324
325 // Hide all custom reason inputs
326 $form.find('.insights-deactivate-form-reason-message').hide().find('input').prop('required', false);
327 // toggle text
328 $form.find('.insights-toggle-text').hide();
329 $selected.find('.insights-toggle-text').show();
330
331 // Show the one under the selected reason, if exists
332 const $customReason = $selected.find('.insights-deactivate-form-reason-message');
333 if ($customReason.length) {
334 $customReason.show().find('input').prop('required', true);
335 }
336 });
337
338 });
339 });
340 </script>
341 <?php
342 });
343
344 }
345
346 private function send_to_server( $track_type, $extra_data = [] ) {
347 if ( $this->is_already_sent( $track_type ) ) {
348 return;
349 }
350
351 $data = [
352 'track_type' => $track_type,
353 'project_type' => $this->type,
354 'project_slug' => $this->slug,
355 'site_url' => site_url(),
356 'admin_email' => get_option( 'admin_email' ),
357 'wp_version' => get_bloginfo( 'version' ),
358 'php_version' => PHP_VERSION,
359 'project_version' => $this->version,
360 'deactivation_reason_type' => '',
361 'deactivation_reason' => '',
362 'deactivation_time' => current_time( 'mysql' ),
363 ];
364
365 if ( $track_type === 'optin' ) {
366 $data['optin_status'] = '';
367 $data['optin_time'] = current_time( 'mysql' );
368 unset( $data['deactivation_reason_type'] );
369 unset( $data['deactivation_reason'] );
370 unset( $data['deactivation_time'] );
371 }
372
373 $payload = \wp_parse_args( $extra_data, $data );
374
375 $api_url = trailingslashit( $this->api_site_url ) . 'wp-json/insightsforwp/v1/track';
376
377 $response = wp_remote_post( $api_url, [
378 'timeout' => 10,
379 'body' => $payload,
380 'sslverify' => false
381 ] );
382
383 if ( is_wp_error( $response ) ) {
384 // Handle error.
385 $error_message = $response->get_error_message();
386 // You can log this or return an error response.
387 error_log( "Request failed: $error_message" );
388 }
389 }
390
391 private function is_already_sent( $type ) {
392 $sent = get_option( 'insightsforwp_sent_data', [] );
393 return ! empty( $sent[ $this->slug ][ $type ] );
394 }
395
396 private function mark_sent( $type ) {
397 $sent = get_option( 'insightsforwp_sent_data', [] );
398 $sent[ $this->slug ][ $type ] = true;
399 update_option( 'insightsforwp_sent_data', $sent, false );
400 }
401
402 public function enqueue_inline_css() {
403 if ( $this->is_already_sent( 'deactivated' ) ) {
404 return;
405 }
406 $css = '
407 .ui-widget-overlay {
408 position: fixed;
409 top: 0;
410 left: 0;
411 width: 100% !important;
412 height: 100% !important;
413 background: rgba(0, 0, 0, 0.5);
414 z-index: 1000;
415 }
416 .insights-deactivate-dialog {
417 display: flex;
418 z-index: 99999;
419 }
420 {{wrapper}} .insights-deactivate-dialog__header {
421 padding: 18px 15px;
422 box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
423 text-align: start;
424 display: flex;
425 }
426 {{wrapper}} .insights-deactivate-dialog__header span {
427 font-size: 15px;
428 text-transform: uppercase;
429 font-weight: bold;
430 padding-inline-start: 5px;
431 color: #515962;
432 }
433 {{wrapper}} .insights-deactivate-dialog__description {
434 font-size: 15px;
435 line-height: 1.4;
436 font-weight: bold;
437 color: #515962;
438 }
439 {{wrapper}} {
440 height: auto;
441 background-color: #ffffff;
442 border-radius: 3px;
443 box-shadow: 2px 8px 23px 3px rgba(0, 0, 0, 0.2);
444 overflow: hidden;
445 }
446
447 {{wrapper}} .insights-deactivate-form {
448 padding: 30px;
449 text-align: start;
450 }
451 {{wrapper}} input[type="radio"] {
452 margin-block: 0;
453 margin-inline: 0 10px;
454 box-shadow: none;
455 }
456 {{wrapper}} label {
457 display: block;
458 font-size: 13px;
459 color: #515962;
460 }
461 {{wrapper}} ul {
462 padding-block-start: 30px;
463 padding-block-end: 15px;
464 }
465 {{wrapper}} .insights-deactivate-form-reason-message {
466 display: none;
467 }
468 {{wrapper}} .insights-deactivate-form-reason-message input {
469 width: 92%;
470 margin-left: 25px;
471 background-color: transparent;
472 color: #515962;
473 padding: 5px;
474 box-shadow: none;
475 }
476 {{wrapper}} .insights-deactivate-form__control-button {
477 display: flex;
478 justify-content: space-between;
479 }
480 {{wrapper}} .insights-deactivate-form__control-button button {
481 font-size: 12px;
482 font-weight: 500;
483 line-height: 1.2;
484 padding: 8px 16px;
485 outline: none;
486 border: none;
487 border-radius: 4px;
488 background-color: white;
489 color: #0c0d0e;
490 transition: all .3s;
491 cursor: pointer;
492 }
493 {{wrapper}} .insights-deactivate-form__control-button button[type="submit"] {
494 background: #13191B;
495 color: #fff;
496 }
497 {{wrapper}} .insights-deactivate-form__control-button button[type="button"] {
498 background: transparent;
499 color: #c4c4c5;
500 }
501 ';
502 wp_add_inline_style( 'wp-admin', str_replace( '{{wrapper}}', '#insights-deactivate-dialog-' . $this->slug, $css ) );
503 }
504 }
505