PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.3.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.3.0
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
cartflows / libraries / nps-survey / classes / nps-survey-script.php

nps-survey-script.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.3.0, at libraries/nps-survey/classes/nps-survey-script.php

649 lines 17.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * NPS Survey Script
4 * File to handle behaviour and content of NPS popup`
5 *
6 * @package {{package}}
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 // Prevent multiple inclusions of this file.
14 if ( defined( 'NPS_SURVEY_SCRIPT_LOADED' ) ) {
15 return;
16 }
17 define( 'NPS_SURVEY_SCRIPT_LOADED', true );
18
19 /**
20 * Nps_Survey
21 */
22 class Nps_Survey {
23 /**
24 * Instance
25 *
26 * @access private
27 * @var object Class Instance.
28 * @since 1.0.0
29 */
30 private static $instance = null;
31
32 /**
33 * Constructor.
34 *
35 * @since 1.0.0
36 */
37 public function __construct() {
38 add_action( 'rest_api_init', array( $this, 'register_route' ) );
39 }
40
41 /**
42 * Initiator
43 *
44 * @since 1.0.0
45 * @return object initialized object of class.
46 */
47 public static function get_instance() {
48 if ( null === self::$instance ) {
49 self::$instance = new self();
50 }
51 return self::$instance;
52 }
53
54 /**
55 * Render NPS Survey.
56 *
57 * @param string $id ID of the root element, should start with nps-survey- .
58 * @param array<mixed> $vars Variables to be passed to the NPS.
59 * @since 1.0.0
60 * @return void
61 */
62 public static function show_nps_notice( string $id, array $vars = [] ): void {
63
64 if ( ! isset( $vars['plugin_slug'] ) || ! is_string( $vars['plugin_slug'] ) ) {
65 return;
66 }
67
68 $plugin_slug = $vars['plugin_slug'];
69 $display_after = is_int( $vars['display_after'] ) ? $vars['display_after'] : 0;
70
71 /**
72 * Filter to check if the NPS survey should be shown.
73 *
74 * @param bool $status Whether to show the notice.
75 * @param string $plugin_slug Plugin slug.
76 * @since 1.0.13
77 */
78 $show_notice = apply_filters(
79 'nps_survey_show_notice',
80 self::is_show_nps_survey_form( $plugin_slug, $display_after ),
81 $plugin_slug
82 );
83
84 if ( ! $show_notice ) {
85 return;
86 }
87
88 $show_on_screen = ! empty( $vars['show_on_screens'] ) && is_array( $vars['show_on_screens'] ) ? $vars['show_on_screens'] : [ 'dashboard' ];
89
90 if ( ! function_exists( 'get_current_screen' ) ) {
91 return;
92 }
93 $current_screen = get_current_screen();
94
95 $admin_only = self::is_nps_survey_enabled_for_admin_only();
96 if ( $admin_only && $current_screen instanceof WP_Screen && ! in_array( $current_screen->id, $show_on_screen, true ) ) {
97 return;
98 }
99 // Loading script here to confirm if the screen is allowed or not.
100 self::editor_load_scripts( $show_on_screen );
101
102 ?><div data-id="<?php echo esc_attr( $id ); ?>" class="nps-survey-root" data-vars="<?php echo esc_attr( strval( wp_json_encode( $vars ) ) ); ?>"></div>
103 <?php
104 }
105
106 /**
107 * Load script.
108 *
109 * @param array<string> $show_on_screens An array of screen IDs where the scripts should be loaded.
110 * @since 1.0.0
111 * @return void
112 */
113 public static function editor_load_scripts( $show_on_screens ): void {
114
115 $admin_only = self::is_nps_survey_enabled_for_admin_only();
116 if ( $admin_only && ! is_admin() ) {
117 return;
118 }
119
120 $screen = get_current_screen();
121 $screen_id = $screen ? $screen->id : '';
122
123 if ( $admin_only && ! in_array( $screen_id, $show_on_screens, true ) ) {
124 return;
125 }
126
127 $handle = 'nps-survey-script';
128 $build_path = NPS_SURVEY_DIR . 'dist/';
129 $default_build_url = NPS_SURVEY_URL . 'dist/';
130
131 // Use a filter to allow $build_url to be modified externally.
132 $build_url = apply_filters( 'nps_survey_build_url', $default_build_url );
133 $script_asset_path = $build_path . 'main.asset.php';
134
135 $script_info = file_exists( $script_asset_path )
136 ? include $script_asset_path
137 : array(
138 'dependencies' => array(),
139 'version' => NPS_SURVEY_VER,
140 );
141
142 $script_dep = array_merge( $script_info['dependencies'], array( 'jquery' ) );
143
144 wp_enqueue_script(
145 $handle,
146 $build_url . 'main.js',
147 $script_dep,
148 $script_info['version'],
149 true
150 );
151
152 $data = apply_filters(
153 'nps_survey_vars',
154 [
155 'ajaxurl' => esc_url( admin_url( 'admin-ajax.php' ) ),
156 '_ajax_nonce' => wp_create_nonce( 'nps-survey' ),
157 'rest_api_nonce' => current_user_can( 'manage_options' ) ? wp_create_nonce( 'wp_rest' ) : '',
158 ]
159 );
160
161 // Add localize JS.
162 wp_localize_script(
163 'nps-survey-script',
164 'nps_survey_data',
165 $data
166 );
167
168 wp_enqueue_style( 'nps-survey-style', $build_url . '/style-main.css', array(), NPS_SURVEY_VER );
169 wp_style_add_data( 'nps-survey-style', 'rtl', 'replace' );
170 wp_enqueue_style( 'nps-survey-fonts', NPS_SURVEY_URL . 'assets/fonts/figtree.css', array(), NPS_SURVEY_VER );
171 }
172
173 /**
174 * Load all the required files in the importer.
175 *
176 * @since 1.0.0
177 * @return void
178 */
179 public static function register_route(): void {
180
181 register_rest_route(
182 self::get_api_namespace(),
183 '/rating/',
184 array(
185 array(
186 'methods' => \WP_REST_Server::CREATABLE,
187 'callback' => array( self::class, 'submit_rating' ),
188 'permission_callback' => array( self::class, 'get_item_permissions_check' ),
189 'args' => array(
190 'nps_id' => array(
191 'type' => 'string',
192 'required' => true,
193 'sanitize_callback' => 'sanitize_key',
194 ),
195 'rating' => array(
196 'type' => 'integer',
197 'required' => true,
198 'validate_callback' => static function ( $value ) {
199 return is_numeric( $value ) && (int) $value >= 0 && (int) $value <= 10;
200 },
201 'sanitize_callback' => 'absint',
202 ),
203 'comment' => array(
204 'type' => 'string',
205 'required' => false,
206 'default' => '',
207 'sanitize_callback' => 'sanitize_text_field',
208 ),
209 'plugin_slug' => array(
210 'type' => 'string',
211 'required' => true,
212 'sanitize_callback' => 'sanitize_key',
213 ),
214 ),
215 ),
216 )
217 );
218
219 register_rest_route(
220 self::get_api_namespace(),
221 '/dismiss-nps-survey/',
222 array(
223 array(
224 'methods' => \WP_REST_Server::CREATABLE,
225 'callback' => array( self::class, 'dismiss_nps_survey_panel' ),
226 'permission_callback' => array( self::class, 'get_item_permissions_check' ),
227 'args' => array(
228 'nps_id' => array(
229 'type' => 'string',
230 'required' => true,
231 'sanitize_callback' => 'sanitize_key',
232 ),
233 'plugin_slug' => array(
234 'type' => 'string',
235 'required' => true,
236 'sanitize_callback' => 'sanitize_key',
237 ),
238 'dismiss_timespan' => array(
239 'type' => 'integer',
240 'required' => true,
241 'sanitize_callback' => 'absint',
242 ),
243 'current_step' => array(
244 'type' => 'string',
245 'required' => true,
246 'sanitize_callback' => 'sanitize_text_field',
247 ),
248 ),
249 ),
250 )
251 );
252 }
253
254 /**
255 * Get the API URL.
256 *
257 * @since 1.0.0
258 *
259 * @return string
260 */
261 public static function get_api_domain() {
262 return trailingslashit( defined( 'NPS_SURVEY_REMOTE_URL' ) ? NPS_SURVEY_REMOTE_URL : apply_filters( 'nps_survey_api_domain', 'https://metrics.brainstormforce.com/' ) );
263 }
264
265 /**
266 * Get api namespace
267 *
268 * @since 1.0.0
269 * @return string
270 */
271 public static function get_api_namespace() {
272 return 'nps-survey/v1';
273 }
274
275 /**
276 * Get API headers
277 *
278 * @since 1.0.0
279 * @return array<string, string>
280 */
281 public static function get_api_headers() {
282 return array(
283 'Content-Type' => 'application/json',
284 'Accept' => 'application/json',
285 );
286 }
287
288 /**
289 * Check whether a given request has permission to read notes.
290 *
291 * @param object $request WP_REST_Request Full details about the request.
292 * @return object|bool
293 */
294 public static function get_item_permissions_check( $request ) {
295 /**
296 * Filter to disable the REST API permission check for NPS Survey endpoints.
297 *
298 * @security WARNING: Setting this filter to `true` removes all authentication
299 * and capability checks from the NPS Survey REST API endpoints, making them
300 * publicly accessible to any unauthenticated request. Only use this in
301 * controlled environments where you explicitly intend to open these endpoints.
302 *
303 * @param bool $disable Whether to bypass the permission check. Default false.
304 * @since 1.0.13
305 */
306 if ( apply_filters( 'nps_survey_api_disable_permission_check', false ) ) {
307 return true;
308 }
309
310 if ( ! current_user_can( 'manage_options' ) ) {
311 return new \WP_Error(
312 'nps_survey_rest_cannot_access',
313 __( 'Sorry, you are not allowed to do that.', 'nps-survey' ),
314 array( 'status' => rest_authorization_required_code() )
315 );
316 }
317 return true;
318 }
319
320 /**
321 * Method to determine if the NPS survey status update should be skipped for database option.
322 *
323 * @param string $nps_id NPS ID.
324 * @param string $type Type of action (e.g., 'submit', 'dismiss').
325 * @param array $data Additional data related to the NPS survey.
326 *
327 * @since 1.0.13
328 * @return bool
329 * @phpstan-ignore-next-line
330 */
331 public static function should_skip_status_update( $nps_id, $type, $data = array() ): bool {
332 /**
333 * Filter to determine if the NPS survey status should be updated.
334 *
335 * @param bool $update Default is true, can be modified by the filter.
336 * @param array $post_data Post data being sent.
337 * @since 1.0.13
338 */
339 return apply_filters(
340 'nps_survey_should_skip_status_update',
341 false, // Default to false, can be modified by the filter.
342 array_merge(
343 $data,
344 array(
345 'nps_id' => $nps_id,
346 'action_type' => $type,
347 )
348 )
349 );
350 }
351
352 /**
353 * Submit Ratings.
354 *
355 * @param \WP_REST_Request<array<string,mixed>> $request Request object.
356 * @return \WP_REST_Response|\WP_Error
357 */
358 public static function submit_rating( $request ) {
359
360 $nonce = $request->get_header( 'X-WP-Nonce' );
361
362 // Verify the nonce.
363 if ( ! wp_verify_nonce( sanitize_text_field( (string) $nonce ), 'wp_rest' ) ) {
364 return new \WP_Error(
365 'nonce_verification_failed',
366 __( 'Nonce verification failed.', 'nps-survey' ),
367 array( 'status' => 403 )
368 );
369 }
370
371 $current_user = wp_get_current_user();
372 $raw_nps_id = $request->get_param( 'nps_id' );
373 $raw_rating = $request->get_param( 'rating' );
374 $raw_comment = $request->get_param( 'comment' );
375 $raw_slug = $request->get_param( 'plugin_slug' );
376 $nps_id = sanitize_key( is_string( $raw_nps_id ) ? $raw_nps_id : '' );
377 $rating = absint( is_numeric( $raw_rating ) ? $raw_rating : 0 );
378 $comment = sanitize_text_field( is_string( $raw_comment ) ? $raw_comment : '' );
379 $plugin_slug = sanitize_key( is_string( $raw_slug ) ? $raw_slug : '' );
380
381 /**
382 * Filter the post data.
383 * This can be used to modify the post data before sending it to the API.
384 *
385 * @param array<mixed> $post_data Post data.
386 * @param string $nps_id NPS ID.
387 * @return array<mixed>
388 */
389 $post_data = apply_filters(
390 'nps_survey_post_data',
391 array(
392 'rating' => $rating,
393 'comment' => $comment,
394 'email' => $current_user->user_email,
395 'first_name' => ! empty( $current_user->first_name ) ? $current_user->first_name : $current_user->display_name,
396 'last_name' => ! empty( $current_user->last_name ) ? $current_user->last_name : '',
397 'source' => $plugin_slug,
398 'plugin_slug' => $plugin_slug,
399 ),
400 $nps_id
401 );
402
403 /**
404 * Filter the API endpoint.
405 *
406 * @param string $api_endpoint API endpoint.
407 * @param array<mixed> $post_data Post data.
408 * @param string $nps_id NPS ID.
409 *
410 * @return string
411 */
412 $api_endpoint = apply_filters(
413 'nps_survey_api_endpoint',
414 self::get_api_domain() . 'wp-json/bsf-metrics-server/v1/nps-survey/',
415 $post_data, // Pass the post data to the filter, so that the endpoint can be modified based on the data.
416 $nps_id
417 );
418
419 $post_data_in_json = wp_json_encode( $post_data );
420 $request_args = array(
421 'body' => $post_data_in_json ? $post_data_in_json : '',
422 'headers' => self::get_api_headers(),
423 'timeout' => 60,
424 );
425
426 $response = wp_safe_remote_post( $api_endpoint, $request_args );
427
428 if ( is_wp_error( $response ) ) {
429 return new \WP_Error(
430 'remote_request_failed',
431 __( 'Remote request failed.', 'nps-survey' ),
432 array( 'status' => 500 )
433 );
434 }
435
436 $response_code = wp_remote_retrieve_response_code( $response );
437
438 if ( 200 === $response_code || 201 === $response_code ) {
439
440 // If the status update should be skipped, return success.
441 if ( self::should_skip_status_update( $nps_id, 'submit', $post_data ) ) {
442 return rest_ensure_response(
443 array(
444 'status' => true,
445 )
446 );
447 }
448
449 $nps_form_status = array(
450 'dismiss_count' => 0,
451 'dismiss_permanently' => true,
452 'dismiss_step' => '',
453 );
454
455 update_option( self::get_nps_id( $plugin_slug ), $nps_form_status, false );
456
457 return rest_ensure_response(
458 array(
459 'status' => true,
460 )
461 );
462
463 } else {
464 return new \WP_Error(
465 'api_error',
466 __( 'Request failed.', 'nps-survey' ),
467 array( 'status' => 500 )
468 );
469 }
470 }
471
472 /**
473 * Dismiss NPS Survey.
474 *
475 * @param \WP_REST_Request<array<string,mixed>> $request Request object.
476 * @return \WP_REST_Response|\WP_Error
477 */
478 public static function dismiss_nps_survey_panel( $request ) {
479
480 $nonce = $request->get_header( 'X-WP-Nonce' );
481
482 // Verify the nonce.
483 if ( ! wp_verify_nonce( sanitize_text_field( (string) $nonce ), 'wp_rest' ) ) {
484 return new \WP_Error(
485 'nonce_verification_failed',
486 __( 'Nonce verification failed.', 'nps-survey' ),
487 array( 'status' => 403 )
488 );
489 }
490
491 // If the status update should be skipped, return success.
492 $raw_nps_id = $request->get_param( 'nps_id' );
493 $raw_slug = $request->get_param( 'plugin_slug' );
494 $raw_timespan = $request->get_param( 'dismiss_timespan' );
495 $raw_step = $request->get_param( 'current_step' );
496 $nps_id = sanitize_key( is_string( $raw_nps_id ) ? $raw_nps_id : '' );
497 $plugin_slug = sanitize_key( is_string( $raw_slug ) ? $raw_slug : '' );
498 $dismiss_timespan = absint( is_numeric( $raw_timespan ) ? $raw_timespan : 0 );
499 $current_step = sanitize_text_field( is_string( $raw_step ) ? $raw_step : '' );
500
501 if ( self::should_skip_status_update( $nps_id, 'dismiss' ) ) {
502 return rest_ensure_response(
503 array(
504 'status' => true,
505 )
506 );
507 }
508
509 $nps_form_status = self::get_nps_survey_dismiss_status( $plugin_slug );
510
511 // Add dismiss timespan.
512 $nps_form_status['dismiss_timespan'] = $dismiss_timespan;
513
514 // Add dismiss date.
515 $nps_form_status['dismiss_time'] = time();
516
517 // Update dismiss count.
518 $nps_form_status['dismiss_count'] += 1;
519 $nps_form_status['dismiss_step'] = $current_step;
520
521 // Dismiss Permanantly.
522 if ( $nps_form_status['dismiss_count'] >= 2 ) {
523 $nps_form_status['dismiss_permanently'] = true;
524 }
525
526 update_option( self::get_nps_id( $plugin_slug ), $nps_form_status, false );
527
528 return rest_ensure_response(
529 array(
530 'status' => true,
531 )
532 );
533 }
534
535 /**
536 * Get dismiss status of NPS Survey.
537 *
538 * @param string $plugin_slug slug of unique NPS Survey.
539 * @return array<string, mixed>
540 */
541 public static function get_nps_survey_dismiss_status( string $plugin_slug ) {
542
543 $default_status = get_option(
544 self::get_nps_id( $plugin_slug ),
545 array(
546 'dismiss_count' => 0,
547 'dismiss_permanently' => false,
548 'dismiss_step' => '',
549 'dismiss_time' => '',
550 'dismiss_timespan' => null,
551 'first_render_time' => null,
552 )
553 );
554
555 if ( ! is_array( $default_status ) ) {
556 return array();
557 }
558
559 return array(
560 'dismiss_count' => ! empty( $default_status['dismiss_count'] ) ? $default_status['dismiss_count'] : 0,
561 'dismiss_permanently' => ! empty( $default_status['dismiss_permanently'] ) ? $default_status['dismiss_permanently'] : false,
562 'dismiss_step' => ! empty( $default_status['dismiss_step'] ) ? $default_status['dismiss_step'] : '',
563 'dismiss_time' => ! empty( $default_status['dismiss_time'] ) ? $default_status['dismiss_time'] : '',
564 'dismiss_timespan' => ! empty( $default_status['dismiss_timespan'] ) ? $default_status['dismiss_timespan'] : null,
565 'first_render_time' => ! empty( $default_status['first_render_time'] ) ? $default_status['first_render_time'] : null,
566 );
567 }
568
569 /**
570 * Show status of NPS Survey.
571 *
572 * @param string $plugin_slug slug of unique NPS Survey.
573 * @param int $display_after number of days after which NPS Survey should be displayed.
574 * @return bool
575 */
576 public static function is_show_nps_survey_form( string $plugin_slug, int $display_after ) {
577
578 $current_time = time();
579 $status = self::get_nps_survey_dismiss_status( $plugin_slug );
580
581 if ( $status['dismiss_permanently'] ) {
582 return false;
583 }
584
585 $first_render_time = $status['first_render_time'];
586
587 if ( 0 !== $display_after ) {
588 if ( null === $first_render_time ) {
589 $status['first_render_time'] = $current_time;
590 update_option( self::get_nps_id( $plugin_slug ), $status );
591 $status = self::get_nps_survey_dismiss_status( $plugin_slug );
592 return false;
593 }
594 if ( $display_after + $first_render_time > $current_time ) {
595 return false;
596 }
597 }
598
599 // Retrieve the stored date time stamp from wp_options.
600 $stored_date_timestamp = $status['dismiss_time'];
601 $dismiss_timespan = $status['dismiss_timespan'];
602
603 if ( $stored_date_timestamp ) {
604
605 $current_time = time();
606
607 // time difference of current time and the time user dismissed the nps.
608 $time_difference = $current_time - $stored_date_timestamp;
609
610 // Check if two weeks have passed.
611 if ( $time_difference <= $dismiss_timespan ) {
612 return false;
613 }
614 }
615
616 return true;
617 }
618
619 /**
620 * Check if NPS Survey is enabled for admin only. Default is true.
621 *
622 * @since 1.0.13
623 * @return bool
624 */
625 public static function is_nps_survey_enabled_for_admin_only() {
626 /**
627 * Filter to check if NPS Survey is enabled for admin only.
628 *
629 * @since 1.0.13
630 */
631 return apply_filters( 'nps_survey_enabled_for_admin_only', true );
632 }
633
634 /**
635 * Get NPS Dismiss Option Name.
636 *
637 * @param string $plugin_slug Plugin name.
638 * @return string
639 */
640 public static function get_nps_id( $plugin_slug ) {
641 return 'nps-survey-' . $plugin_slug;
642 }
643 }
644
645 /**
646 * Kicking this off by calling 'get_instance()' method
647 */
648 Nps_Survey::get_instance();
649