PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.10.9
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.10.9
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / vendor / linno / telemetry / src / Deactivation.php

Deactivation.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.10.9, at vendor/linno/telemetry/src/Deactivation.php

405 lines 18.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace LinnoSDK\Telemetry;
3
4 class Deactivation {
5 /**
6 * Client instance
7 *
8 * @var Client
9 */
10 private Client $client;
11
12 /**
13 * Text domain for i18n
14 *
15 * @var string
16 */
17 private string $textDomain;
18
19 /**
20 * Constructor
21 *
22 * @param Client $client
23 */
24 public function __construct( Client $client ) {
25 $this->client = $client;
26 $this->textDomain = $client->get_text_domain();
27 }
28
29 /**
30 * Initialize the deactivation functionality
31 *
32 * @return void
33 */
34 public function init(): void {
35 add_action( 'admin_footer', array( $this, 'add_deactivation_feedback_modal' ) );
36 add_filter( 'plugin_action_links_' . plugin_basename($this->client->get_plugin_file()), array( $this, 'filter_plugin_action_links' ) );
37 add_action( 'wp_ajax_' . $this->client->get_slug() . '_submit_deactivation_reason', array( $this, 'submit_deactivation_reason' ) );
38 }
39
40 /**
41 * Filter the plugin action links
42 *
43 * @param array $links
44 * @return array
45 */
46 public function filter_plugin_action_links( array $links ): array {
47 if ( isset( $links['deactivate'] ) ) {
48 $links['deactivate'] = str_replace( '<a', '<a class="' . $this->client->get_slug() . '-deactivation-link"', $links['deactivate'] );
49 }
50
51 return $links;
52 }
53
54 /**
55 * Add the deactivation feedback modal to the admin footer
56 *
57 * @return void
58 */
59 public function add_deactivation_feedback_modal(): void {
60 global $pagenow;
61
62 if ( 'plugins.php' !== $pagenow ) {
63 return;
64 }
65
66 $this->deactivation_modal_styles();
67 $reasons = $this->get_uninstall_reasons();
68 $nonce = wp_create_nonce( $this->client->get_slug() . '-deactivation-nonce' );
69 $slug = $this->client->get_slug();
70 ?>
71 <div class="wd-dr-modal" id="<?php echo esc_attr( $slug ); ?>-wd-dr-modal">
72 <div class="wd-dr-modal-wrap">
73 <div class="wd-dr-modal-header">
74 <h3><?php _e( 'Quick question before you go — what\'s the main reason?', $this->textDomain ); ?></h3>
75 <p class="wd-dr-modal-subheading"><?php _e( 'One click is all it takes. Your feedback helps us improve.', $this->textDomain ); ?></p>
76 </div>
77
78 <div class="wd-dr-modal-body">
79 <ul class="wd-de-reasons">
80 <?php foreach ( $reasons as $reason ) { ?>
81 <li data-reason-id="<?php echo esc_attr( $reason['id'] ); ?>">
82 <button type="button" class="wd-de-reason-btn">
83 <div class="wd-de-reason-icon"><?php echo $reason['icon']; ?></div>
84 <div class="wd-de-reason-text"><?php echo esc_html( $reason['text'] ); ?></div>
85 </button>
86 </li>
87 <?php } ?>
88 </ul>
89 </div>
90
91 <div class="wd-dr-modal-footer">
92 <button type="button" class="wd-dr-button-secondary wd-dr-cancel-modal"><?php _e( 'Cancel', $this->textDomain ); ?></button>
93 </div>
94 </div>
95 </div>
96
97 <script type="text/javascript">
98 (function($) {
99 $(function() {
100 var modal = $('#<?php echo esc_js( $slug ); ?>-wd-dr-modal');
101 var deactivateLink = '';
102 var submitting = false;
103
104 // Open modal — capture the real deactivation URL before showing
105 $('#the-list').on('click', 'a.<?php echo esc_js( $slug ); ?>-deactivation-link', function(e) {
106 e.preventDefault();
107 deactivateLink = $(this).attr('href');
108 submitting = false;
109 modal.find('.wd-de-reason-btn').prop('disabled', false);
110 modal.find('li').removeClass('wd-de-reason-selected');
111 modal.addClass('modal-active');
112 });
113
114 // Cancel — close without deactivating
115 modal.on('click', 'button.wd-dr-cancel-modal', function(e) {
116 e.preventDefault();
117 modal.removeClass('modal-active');
118 });
119
120 // One-click reason card → submit reason then deactivate
121 modal.on('click', '.wd-de-reason-btn', function() {
122 if (submitting || !deactivateLink) return;
123 submitting = true;
124
125 var reasonId = $(this).closest('li').data('reason-id');
126
127 $(this).closest('li').addClass('wd-de-reason-selected');
128 modal.find('.wd-de-reason-btn').prop('disabled', true);
129
130 $.ajax({
131 url: ajaxurl,
132 type: 'POST',
133 data: {
134 nonce: '<?php echo esc_js( $nonce ); ?>',
135 action: '<?php echo esc_js( $slug ); ?>_submit_deactivation_reason',
136 reason_id: reasonId,
137 reason_info: ''
138 },
139 complete: function() {
140 window.location.href = deactivateLink;
141 }
142 });
143 });
144 });
145 }(jQuery));
146 </script>
147 <?php
148 }
149
150 /**
151 * Deactivation modal styles
152 */
153 private function deactivation_modal_styles() {
154 ?>
155 <style type="text/css">
156 .wd-dr-modal {
157 position: fixed;
158 z-index: 99999;
159 top: 0;
160 right: 0;
161 bottom: 0;
162 left: 0;
163 background: rgba(0, 0, 0, 0.5);
164 display: none;
165 box-sizing: border-box;
166 overflow: scroll;
167 }
168
169 .wd-dr-modal * {
170 box-sizing: border-box;
171 }
172
173 .wd-dr-modal.modal-active {
174 display: block;
175 }
176
177 .wd-dr-modal-wrap {
178 max-width: 780px;
179 width: 100%;
180 position: relative;
181 margin: 8% auto;
182 background: #fff;
183 border-radius: 8px;
184 box-shadow: 0 8px 32px rgba(0,0,0,0.18);
185 }
186
187 .wd-dr-modal-header {
188 border-bottom: 1px solid #E8E8E8;
189 padding: 24px 24px 16px 24px;
190 }
191
192 .wd-dr-modal-header h3 {
193 line-height: 1.5;
194 margin: 0 0 4px 0;
195 color: #1a1a2e;
196 font-size: 16px;
197 font-weight: 600;
198 }
199
200 .wd-dr-modal-subheading {
201 margin: 0;
202 color: #718096;
203 font-size: 13px;
204 }
205
206 .wd-dr-modal-body {
207 padding: 20px 24px 8px 24px;
208 }
209
210 .wd-dr-modal-footer {
211 border-top: 1px solid #E8E8E8;
212 padding: 16px 24px;
213 display: flex;
214 align-items: center;
215 justify-content: flex-end;
216 }
217
218 ul.wd-de-reasons {
219 display: flex;
220 flex-wrap: wrap;
221 margin: 0 -6px 12px -6px;
222 padding: 0;
223 list-style: none;
224 }
225
226 ul.wd-de-reasons li {
227 padding: 0 6px;
228 margin: 0 0 12px 0;
229 width: 25%;
230 }
231
232 .wd-de-reason-btn {
233 width: 100%;
234 border: 1.5px solid #E8E8E8;
235 border-radius: 6px;
236 background: #fff;
237 cursor: pointer;
238 text-align: center;
239 padding: 14px 8px 12px 8px;
240 transition: border-color 0.15s, background 0.15s, transform 0.1s;
241 display: flex;
242 flex-direction: column;
243 align-items: center;
244 gap: 8px;
245 height: 100%;
246 }
247
248 .wd-de-reason-btn:hover {
249 border-color: #6E42D3;
250 background: #f8f5ff;
251 transform: translateY(-1px);
252 }
253
254 .wd-de-reason-btn:active {
255 transform: translateY(0);
256 }
257
258 .wd-de-reason-btn:disabled {
259 cursor: default;
260 opacity: 0.6;
261 }
262
263 .wd-de-reason-text {
264 color: #4A5568;
265 font-size: 12px;
266 line-height: 1.4;
267 }
268
269 .wd-de-reason-icon {
270 display: flex;
271 align-items: center;
272 justify-content: center;
273 }
274
275 ul.wd-de-reasons li.wd-de-reason-selected .wd-de-reason-btn {
276 background: #6E42D3;
277 border-color: #6E42D3;
278 }
279
280 ul.wd-de-reasons li.wd-de-reason-selected .wd-de-reason-icon svg,
281 ul.wd-de-reasons li.wd-de-reason-selected .wd-de-reason-icon svg g {
282 fill: #fff;
283 }
284
285 ul.wd-de-reasons li.wd-de-reason-selected .wd-de-reason-text {
286 color: #fff;
287 }
288
289 .wd-dr-button-secondary,
290 .wd-dr-button-secondary:hover {
291 border: 1px solid #EBEBEB;
292 border-radius: 4px;
293 font-size: 13px;
294 line-height: 1.5;
295 color: #718096;
296 padding: 6px 14px;
297 cursor: pointer;
298 background-color: transparent;
299 text-decoration: none;
300 }
301 </style>
302 <?php
303 }
304
305 /**
306 * Submit the deactivation reason
307 *
308 * @return void
309 */
310 public function submit_deactivation_reason(): void {
311 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), $this->client->get_slug() . '-deactivation-nonce' ) ) {
312 wp_send_json_error( 'Nonce verification failed' );
313 }
314
315 if ( ! current_user_can( 'manage_options' ) ) {
316 wp_send_json_error( 'You are not allowed for this task' );
317 }
318
319 $reason_id = isset( $_POST['reason_id'] ) ? sanitize_text_field( wp_unslash( $_POST['reason_id'] ) ) : 'none';
320 $reason_info = isset( $_POST['reason_info'] ) ? sanitize_text_field( wp_unslash( $_POST['reason_info'] ) ) : '';
321
322 $this->track_deactivation( $reason_id, $reason_info );
323
324 wp_send_json_success();
325 }
326
327 /**
328 * Track the deactivation event
329 *
330 * @param string $reason_id
331 * @param string $reason_info
332 * @return void
333 */
334 private function track_deactivation( string $reason_id, string $reason_info ): void {
335 $reason = ! empty( $reason_info ) ? $reason_info : $reason_id;
336
337 $this->client->track_lifecycle_event(
338 'activation/plugin_deactivated',
339 [
340 'site_url' => get_site_url(),
341 'reason' => $reason,
342 ]
343 );
344
345 // Set a transient to indicate that a deactivation event has been sent from the feedback form.
346 // This prevents the generic deactivation hook from sending another one.
347 set_transient( $this->client->get_slug() . '_deactivation_event_sent', 'yes', MINUTE_IN_SECONDS );
348 }
349
350 /**
351 * Get the uninstall reasons
352 *
353 * @return array
354 */
355 private function get_uninstall_reasons(): array {
356 $reasons = [
357 [
358 'id' => 'could-not-understand',
359 'text' => __( "Couldn't understand", 'text-domain' ),
360 'placeholder' => __( 'Would you like us to assist you?', 'text-domain' ),
361 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 10.6 23 9.6 22.9 8.8 22.7L8.8 22.6C9.3 22.5 9.7 22.3 10 21.9 10.3 21.6 10.4 21.3 10.4 20.9 10.8 21 11.1 21 11.5 21 16.7 21 21 16.7 21 11.5 21 6.3 16.7 2 11.5 2 6.3 2 2 6.3 2 11.5 2 13 2.3 14.3 2.9 15.6 2.7 16 2.4 16.3 2.2 16.8L2.1 17.1 2.1 17.3C2 17.5 2 17.7 2 18 0.7 16.1 0 13.9 0 11.5 0 5.1 5.1 0 11.5 0ZM6 13.6C6 13.7 6.1 13.8 6.1 13.9 6.3 14.5 6.2 15.7 6.1 16.4 6.1 16.6 6 16.9 6 17.1 6 17.1 6.1 17.1 6.1 17.1 7.1 16.9 8.2 16 9.3 15.5 9.8 15.2 10.4 15 10.9 15 11.2 15 11.4 15 11.6 15.2 11.9 15.4 12.1 16 11.6 16.4 11.5 16.5 11.3 16.6 11.1 16.7 10.5 17 9.9 17.4 9.3 17.7 9 17.9 9 18.1 9.1 18.5 9.2 18.9 9.3 19.4 9.3 19.8 9.4 20.3 9.3 20.8 9 21.2 8.8 21.5 8.5 21.6 8.1 21.7 7.9 21.8 7.6 21.9 7.3 21.9L6.5 22C6.3 22 6 21.9 5.8 21.9 5 21.8 4.4 21.5 3.9 20.9 3.3 20.4 3.1 19.6 3 18.8L3 18.5C3 18.2 3 17.9 3.1 17.7L3.1 17.6C3.2 17.1 3.5 16.7 3.7 16.3 4 15.9 4.2 15.4 4.3 15 4.4 14.6 4.4 14.5 4.6 14.2 4.6 13.9 4.7 13.7 4.9 13.6 5.2 13.2 5.7 13.2 6 13.6ZM11.7 11.2C13.1 11.2 14.3 11.7 15.2 12.9 15.3 13 15.4 13.1 15.4 13.2 15.4 13.4 15.3 13.8 15.2 13.8 15 13.9 14.9 13.8 14.8 13.7 14.6 13.5 14.4 13.2 14.1 13.1 13.5 12.6 12.8 12.3 12 12.2 10.7 12.1 9.5 12.3 8.4 12.8 8.3 12.8 8.2 12.8 8.1 12.8 7.9 12.8 7.8 12.4 7.8 12.2 7.7 12.1 7.8 11.9 8 11.8 8.4 11.7 8.8 11.5 9.2 11.4 10 11.2 10.9 11.1 11.7 11.2ZM16.3 5.9C17.3 5.9 18 6.6 18 7.6 18 8.5 17.3 9.3 16.3 9.3 15.4 9.3 14.7 8.5 14.7 7.6 14.7 6.6 15.4 5.9 16.3 5.9ZM8.3 5C9.2 5 9.9 5.8 9.9 6.7 9.9 7.7 9.2 8.4 8.2 8.4 7.3 8.4 6.6 7.7 6.6 6.7 6.6 5.8 7.3 5 8.3 5Z"/></g></g></svg>',
362 ],
363 [
364 'id' => 'found-better-plugin',
365 'text' => __( 'Found a better plugin', 'text-domain' ),
366 'placeholder' => __( 'Which plugin?', 'text-domain' ),
367 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M17.1 14L22.4 19.3C23.2 20.2 23.2 21.5 22.4 22.4 21.5 23.2 20.2 23.2 19.3 22.4L19.3 22.4 14 17.1C15.3 16.3 16.3 15.3 17.1 14L17.1 14ZM8.6 0C13.4 0 17.3 3.9 17.3 8.6 17.3 13.4 13.4 17.2 8.6 17.2 3.9 17.2 0 13.4 0 8.6 0 3.9 3.9 0 8.6 0ZM8.6 2.2C5.1 2.2 2.2 5.1 2.2 8.6 2.2 12.2 5.1 15.1 8.6 15.1 12.2 15.1 15.1 12.2 15.1 8.6 15.1 5.1 12.2 2.2 8.6 2.2ZM8.6 3.6L8.6 5C6.6 5 5 6.6 5 8.6L5 8.6 3.6 8.6C3.6 5.9 5.9 3.6 8.6 3.6L8.6 3.6Z"/></g></g></svg>',
368 ],
369 [
370 'id' => 'not-have-that-feature',
371 'text' => __( 'Missing a specific feature', 'text-domain' ),
372 'placeholder' => __( 'Could you tell us more about that feature?', 'text-domain' ),
373 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="17" viewBox="0 0 24 17"><g fill="none"><g fill="#3B86FF"><path d="M19.4 0C19.7 0.6 19.8 1.3 19.8 2 19.8 3.2 19.4 4.4 18.5 5.3 17.6 6.2 16.5 6.7 15.2 6.7 15.2 6.7 15.2 6.7 15.2 6.7 14 6.7 12.9 6.2 12 5.3 11.2 4.4 10.7 3.3 10.7 2 10.7 1.3 10.8 0.6 11.1 0L7.6 0 7 0 6.5 0 6.5 5.7C6.3 5.6 5.9 5.3 5.6 5.1 5 4.6 4.3 4.3 3.5 4.3 3.5 4.3 3.5 4.3 3.4 4.3 1.6 4.4 0 5.9 0 7.9 0 8.6 0.2 9.2 0.5 9.7 1.1 10.8 2.2 11.5 3.5 11.5 4.3 11.5 5 11.2 5.6 10.8 6 10.5 6.3 10.3 6.5 10.2L6.5 10.2 6.5 17 6.5 17 7 17 7.6 17 22.5 17C23.3 17 24 16.3 24 15.5L24 0 19.4 0Z"/></g></g></svg>',
374 ],
375 [
376 'id' => 'is-not-working',
377 'text' => __( 'Not working', 'text-domain' ),
378 'placeholder' => __( 'Could you tell us a bit more whats not working?', 'text-domain' ),
379 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 5.1 23 0 17.9 0 11.5 0 5.1 5.1 0 11.5 0ZM11.8 14.4C11.2 14.4 10.7 14.8 10.7 15.4 10.7 16 11.2 16.4 11.8 16.4 12.4 16.4 12.8 16 12.8 15.4 12.8 14.8 12.4 14.4 11.8 14.4ZM12 7C10.1 7 9.1 8.1 9 9.6L10.5 9.6C10.5 8.8 11.1 8.3 11.9 8.3 12.7 8.3 13.2 8.8 13.2 9.5 13.2 10.1 13 10.4 12.2 10.9 11.3 11.4 10.9 12 11 12.9L11 13.4 12.5 13.4 12.5 13C12.5 12.4 12.7 12.1 13.5 11.6 14.4 11.1 14.9 10.4 14.9 9.4 14.9 8 13.7 7 12 7Z"/></g></g></svg>',
380 ],
381 [
382 'id' => 'looking-for-other',
383 'text' => __( 'Not what I was looking', 'text-domain' ),
384 'placeholder' => __( 'Could you tell us a bit more?', 'text-domain' ),
385 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="17" viewBox="0 0 24 17"><g fill="none"><g fill="#3B86FF"><path d="M23.5 9C23.5 9 23.5 8.9 23.5 8.9 23.5 8.9 23.5 8.9 23.5 8.9 23.4 8.6 23.2 8.3 23 8 22.2 6.5 20.6 3.7 19.8 2.6 18.8 1.3 17.7 0 16.1 0 15.7 0 15.3 0.1 14.9 0.2 13.8 0.6 12.6 1.2 12.3 2.7L11.7 2.7C11.4 1.2 10.2 0.6 9.1 0.2 8.7 0.1 8.3 0 7.9 0 6.3 0 5.2 1.3 4.2 2.6 3.4 3.7 1.8 6.5 1 8 0.8 8.3 0.6 8.6 0.5 8.9 0.5 8.9 0.5 8.9 0.5 8.9 0.5 8.9 0.5 9 0.5 9 0.2 9.7 0 10.5 0 11.3 0 14.4 2.5 17 5.5 17 7.3 17 8.8 16.1 9.8 14.8L14.2 14.8C15.2 16.1 16.7 17 18.5 17 21.5 17 24 14.4 24 11.3 24 10.5 23.8 9.7 23.5 9ZM5.5 15C3.6 15 2 13.2 2 11 2 8.8 3.6 7 5.5 7 7.4 7 9 8.8 9 11 9 13.2 7.4 15 5.5 15ZM18.5 15C16.6 15 15 13.2 15 11 15 8.8 16.6 7 18.5 7 20.4 7 22 8.8 22 11 22 13.2 20.4 15 18.5 15Z"/></g></g></svg>',
386 ],
387 [
388 'id' => 'did-not-work-as-expected',
389 'text' => __( "Didn't work as expected", 'text-domain' ),
390 'placeholder' => __( 'What did you expect?', 'text-domain' ),
391 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 5.1 23 0 17.9 0 11.5 0 5.1 5.1 0 11.5 0ZM11.5 2C6.3 2 2 6.3 2 11.5 2 16.7 6.3 21 11.5 21 16.7 21 21 16.7 21 11.5 21 6.3 16.7 2 11.5 2ZM12.5 12.9L12.7 5 10.2 5 10.5 12.9 12.5 12.9ZM11.5 17.4C12.4 17.4 13 16.8 13 15.9 13 15 12.4 14.4 11.5 14.4 10.6 14.4 10 15 10 15.9 10 16.8 10.6 17.4 11.5 17.4Z"/></g></g></svg>',
392 ],
393 [
394 'id' => 'other',
395 'text' => __( 'Others', 'text-domain' ),
396 'placeholder' => __( 'Could you tell us a bit more?', 'text-domain' ),
397 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="23" viewBox="0 0 24 6"><g fill="none"><g fill="#3B86FF"><path d="M3 0C4.7 0 6 1.3 6 3 6 4.7 4.7 6 3 6 1.3 6 0 4.7 0 3 0 1.3 1.3 0 3 0ZM12 0C13.7 0 15 1.3 15 3 15 4.7 13.7 6 12 6 10.3 6 9 4.7 9 3 9 1.3 10.3 0 12 0ZM21 0C22.7 0 24 1.3 24 3 24 4.7 22.7 6 21 6 19.3 6 18 4.7 18 3 18 1.3 19.3 0 21 0Z"/></g></g></svg>',
398 ],
399 ];
400
401 return apply_filters( $this->client->get_slug() . '_telemetry_deactivation_reasons', $reasons );
402 }
403 }
404
405