PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.4.1
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.4.1
5.6.8 5.6.7 5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / kit / nps-survey / classes / nps-survey-script.php
latepoint / lib / kit / nps-survey / classes Last commit date
nps-survey-script.php 3 months ago
nps-survey-script.php
662 lines
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 * Generate and return the Google fonts url.
108 *
109 * @since 1.0.2
110 * @return string
111 */
112 public static function google_fonts_url() {
113
114 $font_families = array(
115 'Figtree:400,500,600,700',
116 );
117
118 $query_args = array(
119 'family' => rawurlencode( implode( '|', $font_families ) ),
120 'subset' => rawurlencode( 'latin,latin-ext' ),
121 );
122
123 return add_query_arg( $query_args, '//fonts.googleapis.com/css' );
124 }
125
126 /**
127 * Load script.
128 *
129 * @param array<string> $show_on_screens An array of screen IDs where the scripts should be loaded.
130 * @since 1.0.0
131 * @return void
132 */
133 public static function editor_load_scripts( $show_on_screens ): void {
134
135 $admin_only = self::is_nps_survey_enabled_for_admin_only();
136 if ( $admin_only && ! is_admin() ) {
137 return;
138 }
139
140 $screen = get_current_screen();
141 $screen_id = $screen ? $screen->id : '';
142
143 if ( $admin_only && ! in_array( $screen_id, $show_on_screens, true ) ) {
144 return;
145 }
146
147 $handle = 'nps-survey-script';
148 $build_path = NPS_SURVEY_DIR . 'dist/';
149 $default_build_url = NPS_SURVEY_URL . 'dist/';
150
151 // Use a filter to allow $build_url to be modified externally.
152 $build_url = apply_filters( 'nps_survey_build_url', $default_build_url );
153 $script_asset_path = $build_path . 'main.asset.php';
154
155 $script_info = file_exists( $script_asset_path )
156 ? include $script_asset_path
157 : array(
158 'dependencies' => array(),
159 'version' => NPS_SURVEY_VER,
160 );
161
162 $script_dep = array_merge( $script_info['dependencies'], array( 'jquery' ) );
163
164 wp_enqueue_script(
165 $handle,
166 $build_url . 'main.js',
167 $script_dep,
168 $script_info['version'],
169 true
170 );
171
172 $data = apply_filters(
173 'nps_survey_vars',
174 [
175 'ajaxurl' => esc_url( admin_url( 'admin-ajax.php' ) ),
176 '_ajax_nonce' => wp_create_nonce( 'nps-survey' ),
177 'rest_api_nonce' => current_user_can( 'manage_options' ) ? wp_create_nonce( 'wp_rest' ) : '',
178 ]
179 );
180
181 // Add localize JS.
182 wp_localize_script(
183 'nps-survey-script',
184 'nps_survey_data',
185 $data
186 );
187
188 wp_enqueue_style( 'nps-survey-style', $build_url . '/style-main.css', array(), NPS_SURVEY_VER );
189 wp_style_add_data( 'nps-survey-style', 'rtl', 'replace' );
190 wp_enqueue_style( 'nps-survey-google-fonts', self::google_fonts_url(), array(), 'all' );
191 }
192
193 /**
194 * Load all the required files in the importer.
195 *
196 * @since 1.0.0
197 * @return void
198 */
199 public static function register_route(): void {
200
201 register_rest_route(
202 self::get_api_namespace(),
203 '/rating/',
204 array(
205 array(
206 'methods' => \WP_REST_Server::CREATABLE,
207 'callback' => array( self::class, 'submit_rating' ),
208 'permission_callback' => array( self::class, 'get_item_permissions_check' ),
209 'args' => array(
210 'nps_id' => array(
211 'type' => 'string',
212 'required' => true,
213 'sanitize_callback' => 'sanitize_text_field',
214 ),
215 'rating' => array(
216 'type' => 'integer',
217 'required' => true,
218 'validate_callback' => static function ( $value ) {
219 return is_numeric( $value ) && (int) $value >= 0 && (int) $value <= 10;
220 },
221 'sanitize_callback' => 'absint',
222 ),
223 'comment' => array(
224 'type' => 'string',
225 'required' => false,
226 'default' => '',
227 'sanitize_callback' => 'sanitize_text_field',
228 ),
229 'plugin_slug' => array(
230 'type' => 'string',
231 'required' => true,
232 'sanitize_callback' => 'sanitize_text_field',
233 ),
234 ),
235 ),
236 )
237 );
238
239 register_rest_route(
240 self::get_api_namespace(),
241 '/dismiss-nps-survey/',
242 array(
243 array(
244 'methods' => \WP_REST_Server::CREATABLE,
245 'callback' => array( self::class, 'dismiss_nps_survey_panel' ),
246 'permission_callback' => array( self::class, 'get_item_permissions_check' ),
247 'args' => array(
248 'nps_id' => array(
249 'type' => 'string',
250 'required' => true,
251 'sanitize_callback' => 'sanitize_text_field',
252 ),
253 'plugin_slug' => array(
254 'type' => 'string',
255 'required' => true,
256 'sanitize_callback' => 'sanitize_text_field',
257 ),
258 'dismiss_timespan' => array(
259 'type' => 'integer',
260 'required' => true,
261 'sanitize_callback' => 'absint',
262 ),
263 'current_step' => array(
264 'type' => 'string',
265 'required' => true,
266 'sanitize_callback' => 'sanitize_text_field',
267 ),
268 ),
269 ),
270 )
271 );
272 }
273
274 /**
275 * Get the API URL.
276 *
277 * @since 1.0.0
278 *
279 * @return string
280 */
281 public static function get_api_domain() {
282 return trailingslashit( defined( 'NPS_SURVEY_REMOTE_URL' ) ? NPS_SURVEY_REMOTE_URL : apply_filters( 'nps_survey_api_domain', 'https://metrics.brainstormforce.com/' ) );
283 }
284
285 /**
286 * Get api namespace
287 *
288 * @since 1.0.0
289 * @return string
290 */
291 public static function get_api_namespace() {
292 return 'nps-survey/v1';
293 }
294
295 /**
296 * Get API headers
297 *
298 * @since 1.0.0
299 * @return array<string, string>
300 */
301 public static function get_api_headers() {
302 return array(
303 'Content-Type' => 'application/json',
304 'Accept' => 'application/json',
305 );
306 }
307
308 /**
309 * Check whether a given request has permission to read notes.
310 *
311 * @param object $request WP_REST_Request Full details about the request.
312 * @return object|bool
313 */
314 public static function get_item_permissions_check( $request ) {
315 /**
316 * Filter to disable the REST API permission check for NPS Survey endpoints.
317 *
318 * @security WARNING: Setting this filter to `true` removes all authentication
319 * and capability checks from the NPS Survey REST API endpoints, making them
320 * publicly accessible to any unauthenticated request. Only use this in
321 * controlled environments where you explicitly intend to open these endpoints.
322 *
323 * @param bool $disable Whether to bypass the permission check. Default false.
324 * @since 1.0.13
325 */
326 if ( apply_filters( 'nps_survey_api_disable_permission_check', false ) ) {
327 return true;
328 }
329
330 if ( ! current_user_can( 'manage_options' ) ) {
331 return new \WP_Error(
332 'nps_survey_rest_cannot_access',
333 __( 'Sorry, you are not allowed to do that.', 'nps-survey' ),
334 array( 'status' => rest_authorization_required_code() )
335 );
336 }
337 return true;
338 }
339
340 /**
341 * Method to determine if the NPS survey status update should be skipped for database option.
342 *
343 * @param string $nps_id NPS ID.
344 * @param string $type Type of action (e.g., 'submit', 'dismiss').
345 * @param array $data Additional data related to the NPS survey.
346 *
347 * @since 1.0.13
348 * @return bool
349 * @phpstan-ignore-next-line
350 */
351 public static function should_skip_status_update( $nps_id, $type, $data = array() ): bool {
352 /**
353 * Filter to determine if the NPS survey status should be updated.
354 *
355 * @param bool $update Default is true, can be modified by the filter.
356 * @param array $post_data Post data being sent.
357 * @since 1.0.13
358 */
359 return apply_filters(
360 'nps_survey_should_skip_status_update',
361 false, // Default to false, can be modified by the filter.
362 array_merge(
363 $data,
364 array(
365 'nps_id' => $nps_id,
366 'action_type' => $type,
367 )
368 )
369 );
370 }
371
372 /**
373 * Submit Ratings.
374 *
375 * @param \WP_REST_Request $request Request object.
376 * @return void
377 * @phpstan-ignore-next-line
378 */
379 public static function submit_rating( $request ) {
380
381 $nonce = $request->get_header( 'X-WP-Nonce' );
382
383 // Verify the nonce.
384 if ( ! wp_verify_nonce( sanitize_text_field( (string) $nonce ), 'wp_rest' ) ) {
385 wp_send_json_error(
386 array(
387 'data' => __( 'Nonce verification failed.', 'nps-survey' ),
388 'status' => false,
389
390 )
391 );
392 }
393
394 $current_user = wp_get_current_user();
395 $nps_id = $request->get_param( 'nps_id' ) ?? '';
396
397 /**
398 * Filter the post data.
399 * This can be used to modify the post data before sending it to the API.
400 *
401 * @param array<mixed> $post_data Post data.
402 * @param string $nps_id NPS ID.
403 * @return array<mixed>
404 */
405 $post_data = apply_filters(
406 'nps_survey_post_data',
407 array(
408 'rating' => ! empty( $request['rating'] ) ? sanitize_text_field( strval( $request['rating'] ) ) : '',
409 'comment' => ! empty( $request['comment'] ) ? sanitize_text_field( strval( $request['comment'] ) ) : '',
410 'email' => $current_user->user_email,
411 'first_name' => ! empty( $current_user->first_name ) ? $current_user->first_name : $current_user->display_name,
412 'last_name' => ! empty( $current_user->last_name ) ? $current_user->last_name : '',
413 'source' => ! empty( $request['plugin_slug'] ) ? sanitize_text_field( strval( $request['plugin_slug'] ) ) : '',
414 'plugin_slug' => ! empty( $request['plugin_slug'] ) ? sanitize_text_field( strval( $request['plugin_slug'] ) ) : '',
415 ),
416 $nps_id
417 );
418
419 /**
420 * Filter the API endpoint.
421 *
422 * @param string $api_endpoint API endpoint.
423 * @param array<mixed> $post_data Post data.
424 * @param string $nps_id NPS ID.
425 *
426 * @return string
427 */
428 $api_endpoint = apply_filters(
429 'nps_survey_api_endpoint',
430 self::get_api_domain() . 'wp-json/bsf-metrics-server/v1/nps-survey/',
431 $post_data, // Pass the post data to the filter, so that the endpoint can be modified based on the data.
432 $nps_id
433 );
434
435 $post_data_in_json = wp_json_encode( $post_data );
436 $request_args = array(
437 'body' => $post_data_in_json ? $post_data_in_json : '',
438 'headers' => self::get_api_headers(),
439 'timeout' => 60,
440 );
441
442 $response = wp_safe_remote_post( $api_endpoint, $request_args );
443
444 if ( is_wp_error( $response ) ) {
445 // There was an error in the request.
446 wp_send_json_error(
447 array(
448 'data' => 'Failed ' . $response->get_error_message(),
449 'status' => false,
450 )
451 );
452 }
453
454 $response_code = wp_remote_retrieve_response_code( $response );
455
456 if ( 200 === $response_code || 201 === $response_code ) {
457
458 // If the status update should be skipped, return success.
459 if ( self::should_skip_status_update( $nps_id, 'submit', $post_data ) ) {
460 wp_send_json_success(
461 array(
462 'status' => true,
463 )
464 );
465 }
466
467 $nps_form_status = array(
468 'dismiss_count' => 0,
469 'dismiss_permanently' => true,
470 'dismiss_step' => '',
471 );
472
473 update_option( self::get_nps_id( strval( $request['plugin_slug'] ) ), $nps_form_status, false );
474
475 wp_send_json_success(
476 array(
477 'status' => true,
478 )
479 );
480
481 } else {
482 wp_send_json_error(
483 array(
484 'status' => false,
485 )
486 );
487 }
488 }
489
490 /**
491 * Dismiss NPS Survey.
492 *
493 * @param \WP_REST_Request $request Request object.
494 * @return void
495 * @phpstan-ignore-next-line
496 */
497 public static function dismiss_nps_survey_panel( $request ) {
498
499 $nonce = $request->get_header( 'X-WP-Nonce' );
500
501 // Verify the nonce.
502 if ( ! wp_verify_nonce( sanitize_text_field( (string) $nonce ), 'wp_rest' ) ) {
503 wp_send_json_error(
504 array(
505 'data' => __( 'Nonce verification failed.', 'nps-survey' ),
506 'status' => false,
507
508 )
509 );
510 }
511
512 // If the status update should be skipped, return success.
513 $nps_id = $request->get_param( 'nps_id' ) ?? '';
514 if ( self::should_skip_status_update( $nps_id, 'dismiss' ) ) {
515 wp_send_json_success(
516 array(
517 'status' => true,
518 )
519 );
520 }
521
522 $nps_form_status = self::get_nps_survey_dismiss_status( strval( $request['plugin_slug'] ) );
523
524 // Add dismiss timespan.
525 $nps_form_status['dismiss_timespan'] = absint( $request['dismiss_timespan'] );
526
527 // Add dismiss date.
528 $nps_form_status['dismiss_time'] = time();
529
530 // Update dismiss count.
531 $nps_form_status['dismiss_count'] += 1;
532 $nps_form_status['dismiss_step'] = sanitize_text_field( strval( $request['current_step'] ) );
533
534 // Dismiss Permanantly.
535 if ( $nps_form_status['dismiss_count'] >= 2 ) {
536 $nps_form_status['dismiss_permanently'] = true;
537 }
538
539 update_option( self::get_nps_id( strval( $request['plugin_slug'] ) ), $nps_form_status );
540
541 wp_send_json_success(
542 array(
543 'status' => true,
544 )
545 );
546 }
547
548 /**
549 * Get dismiss status of NPS Survey.
550 *
551 * @param string $plugin_slug slug of unique NPS Survey.
552 * @return array<string, mixed>
553 */
554 public static function get_nps_survey_dismiss_status( string $plugin_slug ) {
555
556 $default_status = get_option(
557 self::get_nps_id( $plugin_slug ),
558 array(
559 'dismiss_count' => 0,
560 'dismiss_permanently' => false,
561 'dismiss_step' => '',
562 'dismiss_time' => '',
563 'dismiss_timespan' => null,
564 'first_render_time' => null,
565 )
566 );
567
568 if ( ! is_array( $default_status ) ) {
569 return array();
570 }
571
572 return array(
573 'dismiss_count' => ! empty( $default_status['dismiss_count'] ) ? $default_status['dismiss_count'] : 0,
574 'dismiss_permanently' => ! empty( $default_status['dismiss_permanently'] ) ? $default_status['dismiss_permanently'] : false,
575 'dismiss_step' => ! empty( $default_status['dismiss_step'] ) ? $default_status['dismiss_step'] : '',
576 'dismiss_time' => ! empty( $default_status['dismiss_time'] ) ? $default_status['dismiss_time'] : '',
577 'dismiss_timespan' => ! empty( $default_status['dismiss_timespan'] ) ? $default_status['dismiss_timespan'] : null,
578 'first_render_time' => ! empty( $default_status['first_render_time'] ) ? $default_status['first_render_time'] : null,
579 );
580 }
581
582 /**
583 * Show status of NPS Survey.
584 *
585 * @param string $plugin_slug slug of unique NPS Survey.
586 * @param int $display_after number of days after which NPS Survey should be displayed.
587 * @return bool
588 */
589 public static function is_show_nps_survey_form( string $plugin_slug, int $display_after ) {
590
591 $current_time = time();
592 $status = self::get_nps_survey_dismiss_status( $plugin_slug );
593
594 if ( $status['dismiss_permanently'] ) {
595 return false;
596 }
597
598 $first_render_time = $status['first_render_time'];
599
600 if ( 0 !== $display_after ) {
601 if ( null === $first_render_time ) {
602 $status['first_render_time'] = $current_time;
603 update_option( self::get_nps_id( $plugin_slug ), $status );
604 $status = self::get_nps_survey_dismiss_status( $plugin_slug );
605 return false;
606 }
607 if ( $display_after + $first_render_time > $current_time ) {
608 return false;
609 }
610 }
611
612 // Retrieve the stored date time stamp from wp_options.
613 $stored_date_timestamp = $status['dismiss_time'];
614 $dismiss_timespan = $status['dismiss_timespan'];
615
616 if ( $stored_date_timestamp ) {
617
618 $current_time = time();
619
620 // time difference of current time and the time user dismissed the nps.
621 $time_difference = $current_time - $stored_date_timestamp;
622
623 // Check if two weeks have passed.
624 if ( $time_difference <= $dismiss_timespan ) {
625 return false;
626 }
627 }
628
629 return true;
630 }
631
632 /**
633 * Check if NPS Survey is enabled for admin only. Default is true.
634 *
635 * @since 1.0.13
636 * @return bool
637 */
638 public static function is_nps_survey_enabled_for_admin_only() {
639 /**
640 * Filter to check if NPS Survey is enabled for admin only.
641 *
642 * @since 1.0.13
643 */
644 return apply_filters( 'nps_survey_enabled_for_admin_only', true );
645 }
646
647 /**
648 * Get NPS Dismiss Option Name.
649 *
650 * @param string $plugin_slug Plugin name.
651 * @return string
652 */
653 public static function get_nps_id( $plugin_slug ) {
654 return 'nps-survey-' . $plugin_slug;
655 }
656 }
657
658 /**
659 * Kicking this off by calling 'get_instance()' method
660 */
661 Nps_Survey::get_instance();
662