PluginProbe
Hostinger Tools / 2.1.8
Hostinger Tools v2.1.8
3.0.77 3.0.76 3.0.75 3.0.74 3.0.73 3.0.72 3.0.71 3.0.70 3.0.69 3.0.68 3.0.67 3.0.66 1.8.1 1.8.2 1.8.3 1.9.1 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.4 All 108 releases
hostinger / includes / Surveys / Surveys.php

Surveys.php in Hostinger Tools 2.1.8, at includes/Surveys/Surveys.php

548 lines 17.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Hostinger\Surveys;
4
5 use Hostinger\Config;
6 use Hostinger\Helper;
7 use Hostinger\Settings;
8 use Hostinger\Surveys\Questions;
9 use Hostinger\Surveys\Rest\Rest;
10
11 defined( 'ABSPATH' ) || exit;
12
13 class Surveys {
14 public const CACHE_THREE_HOURS = 3600 * 3;
15 public const TIME_15_MINUTES = 900;
16 public const TIME_24_HOURS = 86400;
17 public const CLIENT_SURVEY_IDENTIFIER = 'customer_satisfaction_score';
18 public const CLIENT_WOO_COMPLETED_ACTIONS = 'woocommerce_task_list_tracked_completed_tasks';
19 public const WOO_SURVEY_IDENTIFIER = 'wordpress_woocommerce_onboarding';
20 public const AI_ONBOARDING_SURVEY_IDENTIFIER = 'wordpress_ai_onboarding';
21 public const AFFILIATE_SURVEY_IDENTIFIER = 'wordpress_amazon_affiliate';
22 public const SUBMITTED_SURVEY_TRANSIENT = 'submitted_survey_transient';
23 public const SURVEY_QUESTIONS_TRANSIENT_REQUEST = 'survey_questions_transient_request';
24 public const SURVEY_QUESTIONS_TRANSIENT_RESPONSE = 'survey_questions_transient_response';
25 public const IS_CLIENT_ELIGIBLE_TRANSIENT_RESPONSE = 'client_eligibility_transient_response';
26 public const IS_CLIENT_ELIGIBLE_TRANSIENT_REQUEST = 'survey_questions_transient_request';
27 public const LOCATION_SLUG = 'location';
28 public const WOOCOMMERCE_PAGES = array(
29 '/wp-admin/admin.php?page=wc-admin',
30 '/wp-admin/edit.php?post_type=shop_order',
31 '/wp-admin/admin.php?page=wc-admin&path=/customers',
32 '/wp-admin/edit.php?post_type=shop_coupon&legacy_coupon_menu=1',
33 '/wp-admin/admin.php?page=wc-admin&path=/marketing',
34 '/wp-admin/admin.php?page=wc-reports',
35 '/wp-admin/admin.php?page=wc-settings',
36 '/wp-admin/admin.php?page=wc-status',
37 '/wp-admin/admin.php?page=wc-admin&path=/extensions',
38 '/wp-admin/edit.php?post_type=product',
39 '/wp-admin/post-new.php?post_type=product',
40 '/wp-admin/edit.php?post_type=product&page=product-reviews',
41 '/wp-admin/edit.php?post_type=product&page=product_attributes',
42 '/wp-admin/edit-tags.php?taxonomy=product_cat&post_type=product',
43 '/wp-admin/edit-tags.php?taxonomy=product_tag&post_type=product',
44 '/wp-admin/admin.php?page=wc-admin&path=/analytics/overview',
45 '/wp-admin/admin.php?page=wc-admin',
46 );
47
48 private Config $config_handler;
49 private Settings $settings;
50 private Helper $helper;
51 private Questions $survey_questions;
52 private Rest $surveys_rest;
53
54 public function __construct(
55 Settings $settings,
56 Helper $helper,
57 Config $config_handler,
58 Questions $survey_questions,
59 Rest $surveys_rest
60 ) {
61 $this->settings = $settings;
62 $this->helper = $helper;
63 $this->config_handler = $config_handler;
64 $this->survey_questions = $survey_questions;
65 $this->surveys_rest = $surveys_rest;
66 }
67
68 public function is_woocommerce_admin_page(): bool {
69 if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
70 return false;
71 }
72
73 $current_uri = sanitize_text_field( $_SERVER['REQUEST_URI'] );
74
75 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
76 return false;
77 }
78
79 if ( isset( $current_uri ) && strpos( $current_uri, '/wp-json/' ) !== false ) {
80 return false;
81 }
82
83 foreach ( self::WOOCOMMERCE_PAGES as $page ) {
84 if ( strpos( $current_uri, $page ) !== false ) {
85 return true;
86 }
87 }
88
89 return false;
90 }
91
92 public function is_content_generation_survey_enabled(): bool {
93 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
94 return false;
95 }
96
97 $not_submitted = ! get_transient( self::SUBMITTED_SURVEY_TRANSIENT );
98 $not_completed = ! $this->settings->get_setting( 'feedback_survey_completed' );
99 $content_published = $this->settings->get_setting( 'content_published' );
100 $is_client_eligible = $this->is_client_eligible();
101 $is_hostinger_page = $this->helper->is_hostinger_admin_page();
102
103 if ( ! $is_hostinger_page || empty( $this->get_survey_questions() ) ) {
104 return false;
105 }
106
107 return $not_submitted && $not_completed && $content_published && $is_client_eligible;
108 }
109
110 public function is_woocommerce_survey_enabled(): bool {
111
112 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
113 return false;
114 }
115 $not_submitted = ! get_transient( self::SUBMITTED_SURVEY_TRANSIENT );
116 $not_completed = ! $this->settings->get_setting( 'woocommerce_survey_completed' );
117 $is_woocommerce_page = $this->is_woocommerce_admin_page();
118 $default_woocommerce_completed = $this->helper->default_woocommerce_survey_completed();
119 $oldest_product_date = $this->get_oldest_product_date();
120 $seven_days_ago = strtotime( '-7 days' );
121 $is_client_eligible = $this->is_client_eligible();
122
123 if ( empty( $this->get_survey_questions() ) || $oldest_product_date < $seven_days_ago ) {
124 return false;
125 }
126
127 return $not_submitted && $not_completed && $is_woocommerce_page && $default_woocommerce_completed && $is_client_eligible;
128 }
129
130 public function is_ai_onboarding_survey_enabled(): bool {
131 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
132 return false;
133 }
134
135 $first_login_at = strtotime( get_option( 'hostinger_first_login_at', time() ) );
136 $not_submitted = ! get_transient( self::SUBMITTED_SURVEY_TRANSIENT );
137 $not_completed = ! $this->settings->get_setting( 'ai_onboarding_survey_completed' );
138 $is_client_eligible = $this->is_client_eligible();
139 $is_ai_onboarding_passed = $this->settings->get_setting( 'ai_onboarding' );
140 $is_hostinger_admin_page = $this->helper->is_hostinger_admin_page();
141
142 if ( ! $is_ai_onboarding_passed || empty( $this->get_survey_questions() ) ) {
143 return false;
144 }
145
146 if ( $first_login_at && ! $this->is_time_elapsed( $first_login_at, self::TIME_15_MINUTES ) ) {
147 return false;
148 }
149
150 return $not_submitted && $not_completed && $is_client_eligible && $is_hostinger_admin_page;
151 }
152
153 public function is_affiliate_survey_enabled(): bool {
154
155 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
156 return false;
157 }
158
159 $not_submitted = ! get_transient( self::SUBMITTED_SURVEY_TRANSIENT );
160 $not_completed = ! $this->settings->get_setting( 'affiliate_survey_completed' );
161 $affiliate_links_exsists = $this->settings->get_setting( 'affiliate_links_created' );
162 $is_hostinger_admin_page = $this->helper->is_hostinger_admin_page();
163 $is_client_eligible = $this->is_client_eligible();
164
165 if ( ! $is_hostinger_admin_page || empty( $this->get_survey_questions() ) ) {
166 return false;
167 }
168
169 return $not_submitted && $not_completed && $affiliate_links_exsists && $is_client_eligible;
170 }
171
172 public function is_client_eligible(): bool {
173 $transient_request_key = self::IS_CLIENT_ELIGIBLE_TRANSIENT_REQUEST;
174 $transient_response_key = self::IS_CLIENT_ELIGIBLE_TRANSIENT_RESPONSE;
175 $cached_eligibility = get_transient( $transient_response_key );
176
177 // Check if a request is already in progress
178 if ( get_transient( 'hts_eligible_request' ) ) {
179 return false;
180 }
181
182 // Check if transient response exists
183 if ( $cached_eligibility && ( $cached_eligibility === 'eligible' || $cached_eligibility === 'not_eligible' ) ) {
184 return $cached_eligibility === 'eligible';
185 }
186
187 // Attempt to set transient request
188 if ( ! $this->helper->check_transient_eligibility( $transient_request_key, self::CACHE_THREE_HOURS ) ) {
189 return false;
190 }
191
192 try {
193 // Set transient flag to indicate request in progress
194 set_transient( 'hts_eligible_request', 'in_progress', 60 );
195
196 $is_eligible = $this->surveys_rest->is_client_eligible();
197
198 // Clear the request transient flag
199 delete_transient( 'hts_eligible_request' );
200
201 if ( has_action( 'litespeed_purge_all' ) ) {
202 do_action( 'litespeed_purge_all' );
203 }
204
205 if ( $is_eligible ) {
206 set_transient( $transient_response_key, 'eligible', self::CACHE_THREE_HOURS );
207 return $is_eligible;
208 }
209
210 set_transient( $transient_response_key, 'not_eligible', self::CACHE_THREE_HOURS );
211 return false;
212 } catch ( Exception $exception ) {
213 $this->helper->error_log( 'Error checking eligibility: ' . $exception->getMessage() );
214
215 return false;
216 }
217 }
218
219 public function get_survey_questions(): array {
220 $transient_request_key = self::SURVEY_QUESTIONS_TRANSIENT_REQUEST;
221 $transient_response_key = self::SURVEY_QUESTIONS_TRANSIENT_RESPONSE;
222
223 // Check if a request is already in progress
224 if ( get_transient( 'hts_questions_request' ) ) {
225 return array();
226 }
227
228 // Check if transient response exists
229 $cached_questions = get_transient( $transient_response_key );
230
231 if ( false !== $cached_questions ) {
232 return $cached_questions;
233 }
234
235 // Attempt to set transient request
236 if ( ! $this->helper->check_transient_eligibility( $transient_request_key, self::CACHE_THREE_HOURS ) ) {
237 return array();
238 }
239
240 try {
241 // Set transient flag to indicate request in progress
242 set_transient( 'hts_questions_request', 'in_progress', 60 );
243 $survey_questions = $this->surveys_rest->get_survey_questions();
244
245 // Clear the request transient flag
246 delete_transient( 'hts_questions_request' );
247
248 set_transient( $transient_response_key, $survey_questions, self::CACHE_THREE_HOURS );
249
250 return $survey_questions;
251 } catch ( Exception $exception ) {
252 $this->helper->error_log( 'Error getting survey questions: ' . $exception->getMessage() );
253
254 return array();
255 }
256 }
257
258 public function submit_survey_answers( array $answers, string $survey_type ): void {
259 $required_survey_items = $this->get_required_survey_items( $survey_type );
260 $data = array(
261 'identifier' => self::CLIENT_SURVEY_IDENTIFIER,
262 'answers' => $required_survey_items,
263 );
264
265 $answers = $this->add_user_answers( $data, $answers );
266 $success = $this->surveys_rest->submit_survey_data( $answers, $survey_type );
267
268 set_transient( self::SUBMITTED_SURVEY_TRANSIENT, true, self::TIME_24_HOURS );
269
270 if ( $success ) {
271 delete_transient( self::IS_CLIENT_ELIGIBLE_TRANSIENT_RESPONSE );
272
273 switch ( $survey_type ) {
274 case 'woo_survey':
275 $setting_key = 'woocommerce_survey_completed';
276 break;
277
278 case 'ai_onboarding_survey':
279 $setting_key = 'ai_onboarding_survey_completed';
280 break;
281
282 default:
283 $setting_key = 'feedback_survey_completed';
284 }
285
286 $this->settings->update_setting( $setting_key, true );
287 wp_send_json( __( 'Survey completed', 'hostinger' ) );
288
289 if ( has_action( 'litespeed_purge_all' ) ) {
290 do_action( 'litespeed_purge_all' );
291 }
292 }
293 }
294
295 public function get_wp_survey_questions(): array {
296 $all_questions = $this->get_survey_questions();
297 $question_slugs = array( 'score', 'comment' );
298
299 return $this->filter_questions_by_slug( $all_questions, $question_slugs );
300 }
301
302 public function get_specified_survey_questions( array $survey_questions, string $survey_type = 'ai_survey' ): array {
303 $specified_survey_questions = array(
304 'pages' => array(),
305 'showQuestionNumbers' => 'off',
306 'showTOC' => false,
307 'pageNextText' => __( 'Next', 'hostinger' ),
308 'pagePrevText' => __( 'Previous', 'hostinger' ),
309 'completeText' => __( 'Submit', 'hostinger' ),
310 'completedHtml' => __( 'Thank you for completing the survey !', 'hostinger' ),
311 'requiredText' => '*',
312 );
313
314 foreach ( $survey_questions as $question ) {
315 $title = '';
316 switch ( $survey_type ) {
317 case 'woo_survey':
318 $title = $this->survey_questions->map_survey_questions( $question['slug'] )['woo_question'];
319 break;
320
321 case 'ai_onboarding_survey':
322 $title = $this->survey_questions->map_survey_questions( $question['slug'] )['ai_question'];
323 break;
324
325 case 'affiliate_plugin_survey':
326 $title = $this->survey_questions->map_survey_questions( $question['slug'] )['affiliate_question'];
327 break;
328
329 default:
330 $title = $this->survey_questions->map_survey_questions( $question['slug'] )['question'];
331 break;
332 }
333
334 $element = array(
335 'type' => $this->survey_questions->map_survey_questions( $question['slug'] )['type'],
336 'name' => $question['slug'],
337 'title' => $title,
338 'requiredErrorText' => __( 'Response required.', 'hostinger' ),
339 );
340
341 if ( $question['slug'] == 'comment' ) {
342 $element['maxLength'] = 250;
343 }
344
345 if ( isset( $question['rules'] ) ) {
346 $between_rule = $this->get_between_rule_values( $question['rules'] );
347 if ( $between_rule ) {
348 $element['rateMin'] = $between_rule[0];
349 $element['rateMax'] = $between_rule[1];
350 $element['minRateDescription'] = __( 'Poor', 'hostinger' );
351 $element['maxRateDescription'] = __( 'Excellent', 'hostinger' );
352 }
353
354 if ( $this->is_survey_question_required( $question ) ) {
355 $element['isRequired'] = true;
356 }
357 }
358
359 $question_data = array(
360 'name' => $question['slug'],
361 'elements' => array( $element ),
362 );
363
364 $specified_survey_questions['pages'][] = $question_data;
365 }
366
367 return $specified_survey_questions;
368 }
369
370 public function get_between_rule_values( array $rules ): array {
371 foreach ( $rules as $rule ) {
372 $position = strpos( $rule, 'between:' );
373 if ( $position !== false ) {
374 $between_values = $this->extract_between_values( $rule, $position );
375 if ( count( $between_values ) === 2 ) {
376 return $between_values;
377 }
378 }
379 }
380
381 return array();
382 }
383
384 public function generate_survey_html( string $survey_class ): string {
385 ob_start();
386 ?>
387 <div class="hts-survey-wrapper <?php echo esc_attr( $survey_class ); ?>">
388 <div class="close-btn">
389 <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 24 24">
390 <path d="M 4.9902344 3.9902344 A 1.0001 1.0001 0 0 0 4.2929688 5.7070312 L 10.585938 12 L 4.2929688 18.292969 A 1.0001 1.0001 0 1 0 5.7070312 19.707031 L 12 13.414062 L 18.292969 19.707031 A 1.0001 1.0001 0 1 0 19.707031 18.292969 L 13.414062 12 L 19.707031 5.7070312 A 1.0001 1.0001 0 0 0 18.980469 3.9902344 A 1.0001 1.0001 0 0 0 18.292969 4.2929688 L 12 10.585938 L 5.7070312 4.2929688 A 1.0001 1.0001 0 0 0 4.9902344 3.9902344 z"></path>
391 </svg>
392 </div>
393 <div id="hostinger-feedback-survey"></div>
394 <div id="hts-questionsLeft">
395 <span id="hts-currentQuestion">1</span>
396 <?php
397 echo esc_html(
398 __(
399 'Question',
400 'hostinger'
401 )
402 );
403 ?>
404 <?php echo esc_html( __( 'of ', 'hostinger' ) ); ?>
405 <span id="hts-allQuestions"></span>
406 </div>
407 </div>
408 <?php
409 return ob_get_clean();
410 }
411
412 public function customer_csat_survey(): void {
413 if ( ! did_action( 'customer_csat_survey' ) ) {
414 $survey_html = $this->generate_survey_html( 'hts-woocommerce-csat' );
415 echo $survey_html;
416 do_action( 'customer_csat_survey' );
417 }
418 }
419
420 public function customer_ai_csat_survey(): void {
421 if ( ! did_action( 'customer_ai_csat_survey' ) ) {
422 $survey_html = $this->generate_survey_html( 'hts-ai-onboarding-csat' );
423 echo $survey_html;
424 do_action( 'customer_ai_csat_survey' );
425 }
426 }
427
428 public function ai_plugin_survey(): void {
429 if ( ! did_action( 'ai_plugin_survey' ) ) {
430 $survey_html = $this->generate_survey_html( '' );
431 echo $survey_html;
432 do_action( 'ai_plugin_survey' );
433 }
434 }
435
436 public function affiliate_plugin_survey(): void {
437 if ( ! did_action( 'affiliate_plugin_survey' ) ) {
438 $survey_html = $this->generate_survey_html( 'hts-affiliate-csat' );
439 echo $survey_html;
440 do_action( 'affiliate_plugin_survey' );
441 }
442 }
443
444 private function extract_between_values( string $rule, int $position ): array {
445 $between_prefix_length = strlen( 'between:' );
446 $between_values = explode( ',', substr( $rule, $position + $between_prefix_length ) );
447
448 return count( $between_values ) === 2 ? $between_values : array();
449 }
450
451 public function is_time_elapsed( string $first_login_at, int $time_in_seconds ): bool {
452 $current_time = time();
453 $time_elapsed = $current_time - $time_in_seconds;
454
455 return $time_elapsed >= $first_login_at;
456 }
457
458 private function get_required_survey_items( string $survey_type ): array {
459 switch ( $survey_type ) {
460 case 'woo_survey':
461 return array(
462 array(
463 'question_slug' => self::LOCATION_SLUG,
464 'answer' => self::WOO_SURVEY_IDENTIFIER,
465 ),
466 );
467 case 'ai_onboarding_survey':
468 return array(
469 array(
470 'question_slug' => self::LOCATION_SLUG,
471 'answer' => self::AI_ONBOARDING_SURVEY_IDENTIFIER,
472 ),
473 );
474 case 'affiliate_plugin_survey':
475 return array(
476 array(
477 'question_slug' => self::LOCATION_SLUG,
478 'answer' => self::AFFILIATE_SURVEY_IDENTIFIER,
479 ),
480 );
481 default:
482 return array(
483 array(
484 'question_slug' => self::LOCATION_SLUG,
485 'answer' => 'wordpress_ai_plugin',
486 ),
487 );
488 }
489 }
490
491 private function add_user_answers( array $data, array $answers ): array {
492 foreach ( $answers as $answer_slug => $answer ) {
493 $data['answers'][] = array(
494 'question_slug' => $answer_slug,
495 'answer' => $answer,
496 );
497 }
498
499 return $data;
500 }
501
502 private function filter_questions_by_slug( array $all_questions, array $question_slugs ): array {
503 $questions_with_required_rule = array();
504
505 foreach ( $all_questions as $question ) {
506 if ( isset( $question['slug'] ) && in_array( $question['slug'], $question_slugs ) ) {
507 $questions_with_required_rule[] = array(
508 'slug' => $question['slug'],
509 'rules' => $question['rules'],
510 );
511 }
512 }
513
514 return $questions_with_required_rule;
515 }
516
517 private function is_survey_question_required( array $question ): bool {
518 return isset( $question['rules'] ) && in_array( 'required', $question['rules'] );
519 }
520
521 public function get_oldest_product_date(): int {
522 global $wpdb;
523
524 $get_product_date = $wpdb->prepare(
525 "
526 SELECT MIN(post_date)
527 FROM {$wpdb->posts}
528 WHERE post_type = %s
529 AND post_status = %s
530 ",
531 'product',
532 'publish'
533 );
534
535 $oldest_product_date = $wpdb->get_var( $get_product_date );
536
537 if ( $oldest_product_date !== null ) {
538 $oldest_product_date_timestamp = strtotime( $oldest_product_date );
539
540 if ( $oldest_product_date_timestamp !== false ) {
541 return $oldest_product_date_timestamp;
542 }
543 }
544
545 return strtotime( '-1 year' );
546 }
547 }
548