PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.9.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.9.0
2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 All 96 releases
sureforms / admin / admin.php
admin.php
2,113 lines 71.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Admin Class.
4 *
5 * @package sureforms.
6 */
7
8 namespace SRFM\Admin;
9
10 use Astra_Notices;
11 use SRFM\Inc\AI_Form_Builder\AI_Helper;
12 use SRFM\Inc\Database\Tables\Entries;
13 use SRFM\Inc\Global_Settings\Global_Settings;
14 use SRFM\Inc\Helper;
15 use SRFM\Inc\Onboarding;
16 use SRFM\Inc\Payments\Payment_Helper;
17 use SRFM\Inc\Payments\Stripe\Stripe_Helper;
18 use SRFM\Inc\Smart_Tags;
19 use SRFM\Inc\Traits\Get_Instance;
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit; // Exit if accessed directly.
23 }
24
25 if ( ! class_exists( 'BSF_Admin_Notices' ) ) {
26 require_once SRFM_DIR . 'inc/lib/astra-notices/class-bsf-admin-notices.php';
27 }
28 /**
29 * Admin handler class.
30 *
31 * @since 0.0.1
32 */
33 class Admin {
34 use Get_Instance;
35
36 /**
37 * Minimum number of forms or entries required to show the rating notice.
38 *
39 * @since 2.5.2
40 */
41 public const RATING_NOTICE_THRESHOLD = 3;
42
43 /**
44 * Dashboard widget entries data.
45 *
46 * @var array
47 * @since 1.9.1
48 */
49 private $dashboard_widget_data = [];
50
51 /**
52 * Cached result for whether the rating notice should display.
53 *
54 * @var bool|null
55 * @since 2.5.2
56 */
57 private $should_show_rating = null;
58
59 /**
60 * SureForms Page Default permission.
61 *
62 * @var string
63 * @since 1.12.2
64 */
65 private static $sureforms_page_default_capability = 'manage_options';
66
67 /**
68 * Class constructor.
69 *
70 * @return void
71 * @since 0.0.1
72 */
73 public function __construct() {
74 add_action( 'admin_menu', [ $this, 'add_menu_page' ], 9 );
75 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
76 add_action( 'admin_menu', [ $this, 'settings_page' ] );
77 add_action( 'admin_menu', [ $this, 'add_learn_page' ] );
78 add_action( 'admin_menu', [ $this, 'add_new_form' ] );
79 add_action( 'admin_menu', [ $this, 'add_suremail_page' ] );
80 if ( ! Helper::has_pro() ) {
81 add_action( 'admin_menu', [ $this, 'add_quiz_page' ] );
82 add_action( 'admin_menu', [ $this, 'add_survey_reports_page' ] );
83 add_action( 'admin_menu', [ $this, 'add_partial_entries_page' ] );
84 add_action( 'admin_menu', [ $this, 'add_upgrade_to_pro' ] );
85 add_action( 'admin_footer', [ $this, 'add_upgrade_to_pro_target_attr' ] );
86 }
87
88 add_filter( 'plugin_action_links', [ $this, 'add_settings_link' ], 10, 2 );
89 add_action( 'enqueue_block_assets', [ $this, 'enqueue_styles' ] );
90 add_action( 'admin_head', [ $this, 'enqueue_header_styles' ] );
91 add_filter( 'admin_body_class', [ $this, 'admin_template_picker_body_class' ] );
92
93 // this action is used to restrict Spectra's quick action bar on SureForms CPTS.
94 add_action( 'uag_enable_quick_action_sidebar', [ $this, 'restrict_spectra_quick_action_bar' ] );
95
96 add_action( 'current_screen', [ $this, 'enable_gutenberg_for_sureforms' ], 100 );
97 // Register notices early for React pages (before admin_enqueue_scripts).
98 add_action( 'admin_init', [ $this, 'register_pro_compatibility_notices' ], 5 );
99 // Display notices on traditional WordPress admin pages.
100 add_action( 'admin_notices', [ $this, 'srfm_pro_version_compatibility' ] );
101
102 // Enfold theme compatibility to enable block editor for SureForms post type.
103 add_filter( 'avf_use_block_editor_for_post', [ $this, 'enable_block_editor_in_enfold_theme' ] );
104
105 // Add action links to the plugin page.
106 add_filter( 'plugin_action_links_' . SRFM_BASENAME, [ $this, 'add_action_links' ] );
107 // Check if admin notification is enabled and add entries badge.
108 $general_options = get_option( 'srfm_general_settings_options', [] );
109 $admin_notification_on = isset( $general_options['srfm_admin_notification'] ) ? (bool) $general_options['srfm_admin_notification'] : true;
110
111 if ( $admin_notification_on ) {
112 add_action( 'admin_menu', [ $this, 'maybe_add_entries_badge' ], 99 );
113 }
114
115 add_filter( 'wpforms_current_user_can', [ $this, 'disable_wpforms_capabilities' ], 10, 3 );
116
117 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_pointer' ] );
118 // Ajax callbacks for wp-pointer functionality.
119 add_action( 'wp_ajax_should_show_pointer', [ $this, 'pointer_should_show' ] );
120 add_action( 'wp_ajax_sureforms_dismiss_pointer', [ $this, 'pointer_dismissed' ] );
121 add_action( 'wp_ajax_sureforms_accept_cta', [ $this, 'pointer_accepted_cta' ] );
122 add_action( 'wp_ajax_srfm_notice_response', [ $this, 'handle_notice_response' ] );
123
124 // Register dashboard widget only if there are recent entries.
125 add_action( 'admin_init', [ $this, 'maybe_register_dashboard_widget' ] );
126
127 // Save first form creation time stamp.
128 add_action( 'admin_init', [ $this, 'save_first_form_creation_time_stamp' ] );
129 add_action( 'admin_notices', [ $this, 'display_srfm_rating_notice' ] );
130 add_action( 'admin_notices', [ $this, 'display_srfm_getting_started_notice' ] );
131 }
132
133 /**
134 * Get the first form creation time stamp.
135 *
136 * @since 1.10.1
137 * @return int|false
138 */
139 public static function get_first_form_creation_time_stamp() {
140 return Helper::get_srfm_option( 'first_form_created_at', false );
141 }
142
143 /**
144 * Check if the first form has been created.
145 *
146 * @since 1.10.1
147 * @return bool
148 */
149 public static function is_first_form_created() {
150 // Convert the first form creation time stamp to a boolean. If it exists, it will return true, otherwise false.
151 $first_form_creation_time_stamp = self::get_first_form_creation_time_stamp();
152
153 // If the first form creation time stamp is not set, return false.
154 if ( ! $first_form_creation_time_stamp ) {
155 return false; // No forms created yet.
156 }
157
158 // Check if the first form creation time stamp is a valid integer and greater than zero.
159 return is_int( $first_form_creation_time_stamp ) && $first_form_creation_time_stamp > 0;
160 }
161
162 /**
163 * Check and save the first form creation time stamp.
164 * If not already saved.
165 *
166 * @since 1.10.1
167 * @return void
168 */
169 public static function save_first_form_creation_time_stamp() {
170 if ( ! Helper::current_user_can() || self::is_first_form_created() || ! defined( 'SRFM_FORMS_POST_TYPE' ) || ! post_type_exists( SRFM_FORMS_POST_TYPE ) ) {
171 return;
172 }
173
174 // Get the first form creation time from the database that is published.
175 $query = new \WP_Query(
176 [
177 'post_type' => SRFM_FORMS_POST_TYPE,
178 'posts_per_page' => 1,
179 'orderby' => 'date',
180 'order' => 'ASC',
181 'fields' => 'ids',
182 'post_status' => 'publish',
183 ]
184 );
185
186 if ( ! empty( $query->posts ) && isset( $query->posts[0] ) ) {
187 // Get the first post from the query result.
188 $post_id = $query->posts[0];
189 // Get the post creation time in GMT.
190 $creation_time = get_post_field( 'post_date_gmt', $post_id );
191 // Convert the creation time to a timestamp.
192 $timestamp = strtotime( $creation_time );
193
194 if ( ! $timestamp ) {
195 return;
196 }
197
198 Helper::update_srfm_option( 'first_form_created_at', $timestamp );
199 }
200 }
201
202 /**
203 * Check if n days have passed since the first form creation.
204 * This is used to determine if the dynamic nudges should be shown.
205 *
206 * @param int $days Number of days to check.
207 * @since 1.10.1
208 * @return bool
209 */
210 public static function check_first_form_creation_threshold( $days = 3 ) {
211 $first_form_creation_time_stamp = self::get_first_form_creation_time_stamp();
212
213 if ( ! $first_form_creation_time_stamp ) {
214 return false; // No forms created yet.
215 }
216
217 /**
218 * Calculate the number of days since the first form was created.
219 */
220 $days_from_creation = ( strtotime( current_time( 'mysql' ) ) - $first_form_creation_time_stamp ) / DAY_IN_SECONDS;
221
222 // Return a boolean indicating if the number of days since creation is greater than the specified days.
223 return $days_from_creation > $days;
224 }
225
226 /**
227 * Show action on plugin page.
228 *
229 * @param array $links links.
230 * @return array
231 * @since 1.4.2
232 */
233 public function add_action_links( $links ) {
234 if ( ! Helper::has_pro() ) {
235 // Display upsell link if SureForms Pro is not installed.
236 $upsell_link = Helper::get_sureforms_website_url( 'pricing', [ 'utm_medium' => 'plugin-list' ] );
237
238 ob_start();
239 ?>
240 <a href="<?php echo esc_url( $upsell_link ); ?>" target="_blank" rel="noreferrer" class="sureforms-plugins-go-pro">
241 <?php echo esc_html__( 'Get SureForms Pro', 'sureforms' ); ?>
242 </a>
243 <?php
244 $links[] = trim( ob_get_clean() );
245 }
246
247 return $links;
248 }
249
250 /**
251 * Enable block editor in Enfold theme for SureForms post type.
252 *
253 * @param bool $use_block_editor Whether to use block editor.
254 * @since 1.3.1
255 */
256 public function enable_block_editor_in_enfold_theme( $use_block_editor ) {
257 // if SureForms form post type then return true.
258 if ( SRFM_FORMS_POST_TYPE === get_current_screen()->post_type ) {
259 return true;
260 }
261 return $use_block_editor;
262 }
263
264 /**
265 * Enable Gutenberg for SureForms associated post types.
266 *
267 * @since 0.0.10
268 */
269 public function enable_gutenberg_for_sureforms() {
270 /**
271 * Check if the classic editor is enabled from Classic Editor plugin settings or Divi settings.
272 */
273 if ( 'block' === get_option( 'classic-editor-replace' ) || 'on' === get_option( 'et_enable_classic_editor' ) ) {
274 return;
275 }
276
277 $srfm_post_types = apply_filters( 'srfm_enable_gutenberg_post_types', [ SRFM_FORMS_POST_TYPE ] );
278
279 if ( in_array( get_current_screen()->post_type, $srfm_post_types, true ) ) {
280 add_filter( 'use_block_editor_for_post_type', '__return_true', 110 );
281 add_filter( 'gutenberg_can_edit_post_type', '__return_true', 110 );
282 }
283 }
284
285 /**
286 * Sureforms editor header styles.
287 *
288 * @since 0.0.1
289 */
290 public function enqueue_header_styles() {
291 $current_screen = get_current_screen();
292 $file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
293 $dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
294
295 $css_uri = SRFM_URL . 'assets/css/' . $dir_name . '/';
296
297 /* RTL */
298 if ( is_rtl() ) {
299 $file_prefix .= '-rtl';
300 }
301
302 if ( 'sureforms_form' === $current_screen->id ) {
303 wp_enqueue_style( SRFM_SLUG . '-editor-header-styles', $css_uri . 'header-styles' . $file_prefix . '.css', [], SRFM_VER );
304 }
305 }
306
307 /**
308 * Add menu page.
309 *
310 * @return void
311 * @since 0.0.1
312 */
313 public function add_menu_page() {
314 $menu_slug = 'sureforms_menu';
315
316 $logo = file_get_contents( plugin_dir_path( SRFM_FILE ) . 'images/icon.svg' );
317 add_menu_page(
318 __( 'SureForms', 'sureforms' ),
319 __( 'SureForms', 'sureforms' ),
320 self::$sureforms_page_default_capability,
321 $menu_slug,
322 static function () {
323 },
324 'data:image/svg+xml;base64,' . base64_encode( $logo ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
325 30
326 );
327
328 // Add the Dashboard Submenu.
329 add_submenu_page(
330 $menu_slug,
331 __( 'Dashboard', 'sureforms' ),
332 __( 'Dashboard', 'sureforms' ),
333 self::$sureforms_page_default_capability,
334 $menu_slug,
335 [ $this, 'render_dashboard' ]
336 );
337 }
338
339 /**
340 * Add Settings page.
341 *
342 * @return void
343 * @since 0.0.1
344 */
345 public function settings_page() {
346 $callback = [ $this, 'settings_page_callback' ];
347 add_submenu_page(
348 'sureforms_menu',
349 __( 'Settings', 'sureforms' ),
350 __( 'Settings', 'sureforms' ),
351 self::$sureforms_page_default_capability,
352 'sureforms_form_settings',
353 $callback
354 );
355
356 // Get the current submenu page.
357 $submenu_page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- $_GET['page'] does not provide nonce.
358
359 if ( ! isset( $_GET['tab'] ) && 'sureforms_form_settings' === $submenu_page ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- $_GET['page'] does not provide nonce.
360 wp_safe_redirect( admin_url( 'admin.php?page=sureforms_form_settings&tab=general-settings' ) );
361 exit;
362 }
363 }
364
365 /**
366 * Open to Upgrade to Pro submenu link in new tab.
367 *
368 * @return void
369 * @since 1.6.1
370 */
371 public function add_upgrade_to_pro_target_attr() {
372 ?>
373 <script type="text/javascript">
374 document.addEventListener('DOMContentLoaded', function () {
375 // Upgrade link handler.
376 // IMPORTANT: If this URL changes, also update it in the `add_upgrade_to_pro` function.
377 const upgradeLink = document.querySelector('a[href*="https://sureforms.com/upgrade"]');
378 if (upgradeLink) {
379 upgradeLink.addEventListener('click', e => {
380 e.preventDefault();
381 window.open(upgradeLink.href, '_blank');
382 });
383 }
384 });
385 </script>
386 <?php
387 }
388
389 /**
390 * Add Upgrade to pro menu item.
391 *
392 * @return void
393 * @since 1.6.1
394 */
395 public function add_upgrade_to_pro() {
396 // The url used here is used as a selector for css to style the upgrade to pro submenu.
397 // If you are changing this url, please make sure to update the css as well.
398 $upgrade_url = Helper::get_sureforms_website_url( 'upgrade', [ 'utm_medium' => 'submenu_link_upgrade' ] );
399
400 add_submenu_page(
401 'sureforms_menu',
402 __( 'Upgrade', 'sureforms' ),
403 __( 'Upgrade', 'sureforms' ),
404 self::$sureforms_page_default_capability,
405 $upgrade_url
406 );
407 }
408
409 /**
410 * Add Quiz empty state submenu page for free users.
411 *
412 * @return void
413 * @since 2.7.0
414 */
415 public function add_quiz_page() {
416 add_submenu_page(
417 'sureforms_menu',
418 __( 'Quiz Entries', 'sureforms' ),
419 __( 'Quizzes', 'sureforms' ) .
420 ' <span style="color:#4ADE80;font-size:9px;font-weight:600;">' .
421 esc_html__( 'New', 'sureforms' ) .
422 '</span>',
423 self::$sureforms_page_default_capability,
424 'sureforms_quiz_entries',
425 [ $this, 'render_quiz_empty_state' ],
426 5
427 );
428 }
429
430 /**
431 * Quiz empty state page callback.
432 *
433 * @return void
434 * @since 2.7.0
435 */
436 public function render_quiz_empty_state() {
437 ?>
438 <div id="srfm-quiz-entries-root" class="srfm-admin-wrapper"></div>
439 <?php
440 }
441
442 /**
443 * Add Survey Reports promotional submenu page for free users.
444 *
445 * @return void
446 * @since 2.8.0
447 */
448 public function add_survey_reports_page() {
449 add_submenu_page(
450 'sureforms_menu',
451 __( 'Survey Reports', 'sureforms' ),
452 __( 'Survey Reports', 'sureforms' ) .
453 ' <span style="color:#4ADE80;font-size:9px;font-weight:600;">' .
454 esc_html__( 'New', 'sureforms' ) .
455 '</span>',
456 self::$sureforms_page_default_capability,
457 'sureforms_survey_reports',
458 [ $this, 'render_survey_empty_state' ],
459 6
460 );
461 }
462
463 /**
464 * Survey empty state page callback.
465 *
466 * @return void
467 * @since 2.8.0
468 */
469 public function render_survey_empty_state() {
470 ?>
471 <div id="srfm-survey-empty-state-root" class="srfm-admin-wrapper"></div>
472 <?php
473 }
474
475 /**
476 * Add Partial Entries promotional submenu page for free users.
477 *
478 * @return void
479 * @since 2.9.0
480 */
481 public function add_partial_entries_page() {
482 add_submenu_page(
483 'sureforms_menu',
484 __( 'Partial Entries', 'sureforms' ),
485 __( 'Partial Entries', 'sureforms' ) .
486 ' <span style="color:#4ADE80;font-size:9px;font-weight:600;">' .
487 esc_html__( 'New', 'sureforms' ) .
488 '</span>',
489 self::$sureforms_page_default_capability,
490 'sureforms_partial_entries',
491 [ $this, 'render_partial_entries_empty_state' ],
492 7
493 );
494 }
495
496 /**
497 * Partial Entries empty state page callback.
498 *
499 * @return void
500 * @since 2.9.0
501 */
502 public function render_partial_entries_empty_state() {
503 ?>
504 <div id="srfm-partial-entries-empty-state-root" class="srfm-admin-wrapper"></div>
505 <?php
506 }
507
508 /**
509 * Add SMTP promotional submenu page.
510 *
511 * @return void
512 * @since 1.7.1
513 */
514 public function add_suremail_page() {
515 add_submenu_page(
516 'sureforms_menu',
517 __( 'SMTP', 'sureforms' ),
518 __( 'SMTP', 'sureforms' ),
519 self::$sureforms_page_default_capability,
520 'sureforms_smtp',
521 [ $this, 'suremail_page_callback' ]
522 );
523
524 // Get the current submenu page.
525 $submenu_page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- $_GET['page'] does not provide nonce.
526
527 // Check if SureMail is installed and active.
528 if ( 'sureforms_smtp' === $submenu_page && file_exists( WP_PLUGIN_DIR . '/suremails/suremails.php' ) && is_plugin_active( 'suremails/suremails.php' ) ) {
529 // Plugin is installed and active - redirect to SureMail dashboard.
530 wp_safe_redirect( admin_url( 'options-general.php?page=suremail#/dashboard' ) );
531 exit;
532 }
533 }
534
535 /**
536 * SMTP promotional page callback.
537 *
538 * @return void
539 * @since 1.7.1
540 */
541 public function suremail_page_callback() {
542 ?>
543 <div id="srfm-suremail-container" class="srfm-admin-wrapper"></div>
544 <?php
545 }
546
547 /**
548 * Render Admin Dashboard.
549 *
550 * @return void
551 * @since 0.0.1
552 */
553 public function render_dashboard() {
554 ?>
555 <div id="srfm-dashboard-container" class="srfm-admin-wrapper"></div>
556 <?php
557 }
558
559 /**
560 * Settings page callback.
561 *
562 * @return void
563 * @since 0.0.1
564 */
565 public function settings_page_callback() {
566 ?>
567 <div id="srfm-settings-container" class="srfm-admin-wrapper"></div>
568 <?php
569 }
570
571 /**
572 * Add Learn submenu page.
573 *
574 * @return void
575 * @since 2.5.2
576 */
577 public function add_learn_page() {
578 add_submenu_page(
579 'sureforms_menu',
580 __( 'Learn', 'sureforms' ),
581 __( 'Learn', 'sureforms' ),
582 self::$sureforms_page_default_capability,
583 'sureforms_learn',
584 [ $this, 'render_learn' ]
585 );
586 }
587
588 /**
589 * Learn page callback.
590 *
591 * @return void
592 * @since 2.5.2
593 */
594 public function render_learn() {
595 ?>
596 <div id="srfm-learn-root" class="srfm-admin-wrapper"></div>
597 <?php
598 }
599
600 /**
601 * Add new form menu item.
602 *
603 * @return void
604 * @since 0.0.1
605 */
606 public function add_new_form() {
607 add_submenu_page(
608 'sureforms_menu',
609 __( 'Forms', 'sureforms' ),
610 __( 'Forms', 'sureforms' ),
611 self::$sureforms_page_default_capability,
612 'sureforms_forms',
613 [ $this, 'render_forms' ],
614 1
615 );
616 add_submenu_page(
617 'sureforms_menu',
618 __( 'New Form', 'sureforms' ),
619 __( 'New Form', 'sureforms' ),
620 self::$sureforms_page_default_capability,
621 'add-new-form',
622 [ $this, 'add_new_form_callback' ],
623 2
624 );
625 $entries_hook = add_submenu_page(
626 'sureforms_menu',
627 __( 'Entries', 'sureforms' ),
628 __( 'Entries', 'sureforms' ),
629 self::$sureforms_page_default_capability,
630 SRFM_ENTRIES,
631 [ $this, 'render_entries' ],
632 3
633 );
634
635 add_submenu_page(
636 'sureforms_menu',
637 __( 'Payments', 'sureforms' ),
638 __( 'Payments', 'sureforms' ),
639 self::$sureforms_page_default_capability,
640 SRFM_PAYMENTS,
641 [ $this, 'render_payments' ],
642 4
643 );
644
645 if ( $entries_hook ) {
646 add_action( 'load-' . $entries_hook, [ $this, 'mark_entries_page_visit' ] );
647 }
648 }
649
650 /**
651 * Payments page callback.
652 *
653 * @return void
654 * @since 2.0.0
655 */
656 public function render_payments() {
657 ?>
658 <div id="srfm-payments-react-container" class="srfm-admin-wrapper"></div>
659 <?php
660 }
661
662 /**
663 * Add new form mentu item callback.
664 *
665 * @return void
666 * @since 0.0.1
667 */
668 public function add_new_form_callback() {
669 ?>
670 <div id="srfm-add-new-form-container" class="srfm-admin-wrapper"></div>
671 <?php
672 }
673
674 /**
675 * Forms page callback.
676 *
677 * @return void
678 * @since 2.0.0
679 */
680 public function render_forms() {
681 ?>
682 <div id="srfm-forms-root" class="srfm-admin-wrapper"></div>
683 <?php
684 }
685
686 /**
687 * Entries page callback.
688 *
689 * @since 0.0.13
690 * @since 2.0.0 - Updated the entries UI and the function definition.
691 * @return void
692 */
693 public function render_entries() {
694 echo '<div id="srfm-entries-root"></div>';
695 }
696
697 /**
698 * Add notification badge to SureForms menu when there are new entries.
699 *
700 * @since 1.7.3
701 * @return void
702 */
703 public function maybe_add_entries_badge() {
704 if ( ! Helper::current_user_can() ) {
705 return;
706 }
707
708 // If currently viewing the entries listing page, mark it as visited and skip the badge.
709 if ( isset( $_GET['page'] ) && SRFM_ENTRIES === $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking the page slug.
710 $this->mark_entries_page_visit();
711 return;
712 }
713
714 $srfm_options = get_option( 'srfm_options', [] );
715 $last_visit = isset( $srfm_options['entries_last_visited'] ) ? absint( $srfm_options['entries_last_visited'] ) : 0;
716 $new_entries = Entries::get_entries_count_after( $last_visit );
717
718 if ( $new_entries <= 0 ) {
719 return;
720 }
721
722 global $menu;
723 foreach ( $menu as $index => $item ) {
724 if ( isset( $item[2] ) && 'sureforms_menu' === $item[2] ) {
725 ob_start();
726 ?>
727 <span class="srfm-update-dot"></span>
728 <?php
729 $dot_html = ob_get_clean();
730 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Adding notifications for menu item.
731 $menu[ $index ][0] .= $dot_html;
732 break;
733 }
734 }
735
736 global $submenu;
737 if ( isset( $submenu['sureforms_menu'] ) ) {
738 foreach ( $submenu['sureforms_menu'] as $index => $sub_item ) {
739 if ( isset( $sub_item[2] ) && SRFM_ENTRIES === $sub_item[2] ) {
740 ob_start();
741 ?>
742 <span class="update-plugins count-<?php echo absint( $new_entries ); ?>">
743 <span class="plugin-count"><?php echo absint( $new_entries ); ?></span>
744 </span>
745 <?php
746 $badge_html = ob_get_clean();
747 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Adding notifications for submenu item.
748 $submenu['sureforms_menu'][ $index ][0] .= $badge_html;
749 break;
750 }
751 }
752 }
753 }
754
755 /**
756 * Mark the user's visit to the entries page.
757 *
758 * @since 1.7.3
759 * @return void
760 */
761 public function mark_entries_page_visit() {
762 if ( Helper::current_user_can() ) {
763 $srfm_options = get_option( 'srfm_options', [] );
764 $srfm_options['entries_last_visited'] = time();
765 \SRFM\Inc\Helper::update_admin_settings_option( 'srfm_options', $srfm_options );
766 }
767 }
768
769 /**
770 * Adds a settings link to the plugin action links on the plugins page.
771 *
772 * @param array $links An array of plugin action links.
773 * @param string $file The plugin file path.
774 * @return array The updated array of plugin action links.
775 * @since 0.0.1
776 */
777 public function add_settings_link( $links, $file ) {
778 if ( 'sureforms/sureforms.php' === $file ) {
779 ob_start();
780 ?>
781 <a href="<?php echo esc_url( admin_url( 'admin.php?page=sureforms_form_settings&tab=general-settings' ) ); ?>">
782 <?php echo esc_html__( 'Settings', 'sureforms' ); ?>
783 </a>
784 <?php
785 $settings_link_html = ob_get_clean();
786 $plugin_links = apply_filters(
787 'sureforms_plugin_action_links',
788 [
789 'sureforms_settings' => $settings_link_html,
790 ]
791 );
792 $links = array_merge( $plugin_links, $links );
793 }
794 return $links;
795 }
796
797 /**
798 * Sureforms block editor styles.
799 *
800 * @since 0.0.1
801 */
802 public function enqueue_styles() {
803 $current_screen = get_current_screen();
804 global $wp_version;
805
806 $file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
807 $dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
808
809 $css_uri = SRFM_URL . 'assets/css/' . $dir_name . '/';
810 $vendor_css_uri = SRFM_URL . 'assets/css/minified/deps/';
811
812 /* RTL */
813 if ( is_rtl() ) {
814 $file_prefix .= '-rtl';
815 }
816
817 // Enqueue editor styles for post and page.
818 if ( SRFM_FORMS_POST_TYPE === $current_screen->post_type ) {
819 wp_enqueue_style( SRFM_SLUG . '-editor', $css_uri . 'backend/editor' . $file_prefix . '.css', [], SRFM_VER );
820 wp_enqueue_style( SRFM_SLUG . '-backend-blocks', $css_uri . 'blocks/default/backend' . $file_prefix . '.css', [], SRFM_VER );
821 wp_enqueue_style( SRFM_SLUG . '-intl', $vendor_css_uri . 'intl/intlTelInput-backend.min.css', [], SRFM_VER );
822 wp_enqueue_style( SRFM_SLUG . '-common', $css_uri . 'common' . $file_prefix . '.css', [], SRFM_VER );
823 wp_enqueue_style( SRFM_SLUG . '-reactQuill', $vendor_css_uri . 'quill/quill.snow.css', [], SRFM_VER );
824 wp_enqueue_style( SRFM_SLUG . '-single-form-modal', $css_uri . 'single-form-setting' . $file_prefix . '.css', [], SRFM_VER );
825
826 // if version is equal to or lower than 6.6.2 then add compatibility css.
827 if ( version_compare( $wp_version, '6.6.2', '<=' ) ) {
828 $srfm_inline_css = '.srfm-settings-modal .srfm-setting-modal-container .components-toggle-control .components-base-control__help{
829 margin-left: 4em;
830 }';
831 wp_add_inline_style( SRFM_SLUG . '-single-form-modal', $srfm_inline_css );
832 }
833 }
834
835 wp_enqueue_style( SRFM_SLUG . '-form-selector', $css_uri . 'srfm-form-selector' . $file_prefix . '.css', [], SRFM_VER );
836 wp_enqueue_style( SRFM_SLUG . '-common-editor', SRFM_URL . 'assets/build/common-editor.css', [], SRFM_VER, 'all' );
837 }
838
839 /**
840 * Get Breadcrumbs for current page.
841 *
842 * @since 0.0.1
843 * @return array Breadcrumbs Array.
844 */
845 public function get_breadcrumbs_for_current_page() {
846 global $post, $pagenow;
847 $breadcrumbs = [];
848
849 if ( 'admin.php' === $pagenow && isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We don't need nonce verification here.
850 $page_title = get_admin_page_title();
851 $breadcrumbs[] = [
852 'title' => $page_title,
853 'link' => '',
854 ];
855 } elseif ( $post && in_array( $pagenow, [ 'post.php', 'post-new.php', 'edit.php' ], true ) ) {
856 $post_type_obj = get_post_type_object( get_post_type() );
857 if ( $post_type_obj ) {
858 $post_type_plural = $post_type_obj->labels->name;
859 $breadcrumbs[] = [
860 'title' => $post_type_plural,
861 'link' => admin_url( 'edit.php?post_type=' . $post_type_obj->name ),
862 ];
863
864 if ( 'edit.php' === $pagenow && ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We don't need nonce verification here.
865 $breadcrumbs[ count( $breadcrumbs ) - 1 ]['link'] = '';
866 } else {
867 $breadcrumbs[] = [
868 /* Translators: Post Title. */
869 'title' => sprintf( __( 'Edit %1$s', 'sureforms' ), get_the_title() ),
870 'link' => get_edit_post_link( $post->ID ),
871 ];
872 }
873 }
874 } else {
875 $current_screen = get_current_screen();
876 if ( $current_screen && 'sureforms_form' === $current_screen->post_type ) {
877 $breadcrumbs[] = [
878 'title' => 'Forms',
879 'link' => '',
880 ];
881 } else {
882 $breadcrumbs[] = [
883 'title' => '',
884 'link' => '',
885 ];
886 }
887 }
888
889 return $breadcrumbs;
890 }
891
892 /**
893 * Enqueue Admin Scripts.
894 *
895 * @return void
896 * @since 0.0.1
897 */
898 public function enqueue_scripts() {
899 $current_screen = get_current_screen();
900 global $wp_version;
901
902 $file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
903 $dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
904 $css_uri = SRFM_URL . 'assets/css/' . $dir_name . '/';
905 $is_rtl = is_rtl();
906 $rtl = $is_rtl ? '-rtl' : '';
907
908 /**
909 * List of the handles in which we need to add translation compatibility.
910 */
911 $script_translations_handlers = [];
912 $onboarding_instance = Onboarding::get_instance();
913 $current_user = wp_get_current_user();
914
915 $localization_data = [
916 'site_url' => get_site_url(),
917 'current_user_login' => $current_user->user_login ?? '',
918 'website_lead_details' => [
919 'first_name' => $current_user->first_name ?? '',
920 'last_name' => $current_user->last_name ?? '',
921 'email' => $current_user->user_email ?? '',
922 ],
923 'breadcrumbs' => $this->get_breadcrumbs_for_current_page(),
924 'sureforms_dashboard_url' => admin_url( '/admin.php?page=sureforms_menu' ),
925 'plugin_version' => SRFM_VER,
926 'global_settings_nonce' => Helper::current_user_can() ? wp_create_nonce( 'wp_rest' ) : '',
927 'is_pro_active' => Helper::has_pro(),
928 'is_first_form_created' => self::is_first_form_created(),
929 'check_three_days_threshold' => self::check_first_form_creation_threshold(),
930 'check_eight_days_threshold' => self::check_first_form_creation_threshold( 8 ),
931 'pro_plugin_version' => Helper::has_pro() ? SRFM_PRO_VER : '',
932 'pro_plugin_name' => Helper::has_pro() && defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'SureForms Pro',
933 'sureforms_pricing_page' => Helper::get_sureforms_website_url( 'pricing' ),
934 'field_spacing_vars' => Helper::get_css_vars(),
935 'is_ver_lower_than_6_7' => version_compare( $wp_version, '6.6.2', '<=' ),
936 'integrations' => Helper::sureforms_get_integration(),
937 'rotating_plugin_banner' => Helper::get_rotating_plugin_banner(),
938 'ajax_url' => admin_url( 'admin-ajax.php' ),
939 'sf_plugin_manager_nonce' => wp_create_nonce( 'sf_plugin_manager_nonce' ),
940 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
941 'plugin_activating_text' => __( 'Activating...', 'sureforms' ),
942 'plugin_activated_text' => __( 'Activated', 'sureforms' ),
943 'plugin_activate_text' => __( 'Activate', 'sureforms' ),
944 'plugin_installing_text' => __( 'Installing...', 'sureforms' ),
945 'plugin_installed_text' => __( 'Installed', 'sureforms' ),
946 'privacy_policy_url' => Helper::get_sureforms_website_url( 'privacy-policy/' ),
947 'is_rtl' => $is_rtl,
948 'onboarding_completed' => method_exists( $onboarding_instance, 'get_onboarding_status' ) ? $onboarding_instance->get_onboarding_status() : false,
949 'onboarding_redirect' => isset( $_GET['srfm-activation-redirect'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required for the activation redirection.
950 'pointer_nonce' => wp_create_nonce( 'sureforms_pointer_action' ),
951 'general_settings_url' => admin_url( '/options-general.php' ),
952 'additional_header_nav_items' => [],
953 // Smart tags for the Global Defaults email notification fields.
954 // srfm_block_data is only available in the block editor; these keys
955 // make the same data accessible on the settings page via srfm_admin.
956 'smart_tags_array' => Smart_Tags::smart_tag_list(),
957 'smart_tags_array_email' => Smart_Tags::email_smart_tag_list(),
958 // Default confirmation message HTML (icon + heading + text) used as
959 // the initial React state before the settings API response arrives.
960 'default_confirmation_message' => Global_Settings::get_default_confirmation_message(),
961 'payments' => apply_filters(
962 'srfm_admin_localize_payments_data',
963 [
964 'stripe_connected' => Stripe_Helper::is_stripe_connected(),
965 'stripe_mode' => Stripe_Helper::get_stripe_mode(),
966 'stripe_connect_url' => Stripe_Helper::get_stripe_settings_url(),
967 'currencies_data' => Payment_Helper::get_all_currencies_data(),
968 'zero_decimal_currencies' => Payment_Helper::get_zero_decimal_currencies(),
969 'webhook_url' => Stripe_Helper::get_webhook_url(),
970 'webhook_test_connected' => Stripe_Helper::is_webhook_configured( 'test', true ),
971 'webhook_live_connected' => Stripe_Helper::is_webhook_configured( 'live', true ),
972 'is_transaction_present' => Stripe_Helper::is_transaction_present(),
973 'payment_currency' => Payment_Helper::get_currency(),
974 'currency_sign_position' => Payment_Helper::get_currency_sign_position(),
975 ]
976 ),
977 'mcp_adapter_status' => file_exists( WP_PLUGIN_DIR . '/mcp-adapter/mcp-adapter.php' )
978 ? ( is_plugin_active( 'mcp-adapter/mcp-adapter.php' ) ? 'active' : 'installed' )
979 : 'not_installed',
980 'mcp_endpoint_url' => esc_url_raw( rest_url( 'sureforms/v1/mcp' ) ),
981 ];
982
983 $is_screen_sureforms_menu = Helper::validate_request_context( 'sureforms_menu', 'page' );
984 $is_screen_add_new_form = Helper::validate_request_context( 'add-new-form', 'page' );
985 $is_screen_sureforms_forms = Helper::validate_request_context( 'sureforms_forms', 'page' );
986 $is_screen_sureforms_form_settings = Helper::validate_request_context( 'sureforms_form_settings', 'page' );
987 $is_screen_sureforms_payments = Helper::validate_request_context( 'sureforms_payments', 'page' );
988 $is_screen_sureforms_entries = Helper::validate_request_context( SRFM_ENTRIES, 'page' );
989 $is_screen_sureforms_learn = Helper::validate_request_context( 'sureforms_learn', 'page' );
990 $is_screen_quiz_empty_state = Helper::validate_request_context( 'sureforms_quiz_entries', 'page' );
991 $is_screen_survey_empty_state = Helper::validate_request_context( 'sureforms_survey_reports', 'page' );
992 $is_screen_partial_entries_empty_state = Helper::validate_request_context( 'sureforms_partial_entries', 'page' );
993 $is_post_type_sureforms_form = SRFM_FORMS_POST_TYPE === $current_screen->post_type;
994
995 /**
996 * Check if the current screen is the SureForms Menu and AI Auth Email is present then we will add user type as registered.
997 * Compatibility with existing UI code that checks for this condition.
998 */
999 if ( $is_screen_sureforms_menu ) {
1000 // If email is stored send the user type as registered else non-registered.
1001 $localization_data['srfm_ai_details'] = [
1002 'type' => ! empty( get_option( 'srfm_ai_auth_user_email' ) ) ? 'registered' : 'non-registered',
1003 ];
1004 }
1005
1006 // Add the Quizzes and Survey Reports nav items when pro is not active.
1007 if ( ! Helper::has_pro() ) {
1008 $localization_data['additional_header_nav_items'][] = [
1009 'slug' => 'sureforms_quiz_entries',
1010 'text' => __( 'Quizzes', 'sureforms' ),
1011 'link' => admin_url( 'admin.php?page=sureforms_quiz_entries' ),
1012 ];
1013 $localization_data['additional_header_nav_items'][] = [
1014 'slug' => 'sureforms_survey_reports',
1015 'text' => __( 'Survey Reports', 'sureforms' ),
1016 'link' => admin_url( 'admin.php?page=sureforms_survey_reports' ),
1017 ];
1018 $localization_data['additional_header_nav_items'][] = [
1019 'slug' => 'sureforms_partial_entries',
1020 'text' => __( 'Partial Entries', 'sureforms' ),
1021 'link' => admin_url( 'admin.php?page=sureforms_partial_entries' ),
1022 ];
1023 }
1024
1025 $is_sureforms_screen = $is_screen_sureforms_menu || $is_post_type_sureforms_form || $is_screen_add_new_form || $is_screen_sureforms_forms || $is_screen_sureforms_form_settings || $is_screen_sureforms_entries || $is_screen_sureforms_payments || $is_screen_sureforms_learn || $is_screen_quiz_empty_state || $is_screen_survey_empty_state || $is_screen_partial_entries_empty_state;
1026
1027 /**
1028 * Filter to allow extending the SureForms dashboard screen check.
1029 *
1030 * @since 2.6.0
1031 *
1032 * @param bool $is_sureforms_screen Whether the current screen is a SureForms dashboard screen.
1033 */
1034 $is_sureforms_screen = apply_filters( 'srfm_is_dashboard_screen', $is_sureforms_screen );
1035
1036 if ( $is_sureforms_screen ) {
1037 $asset_handle = '-dashboard';
1038
1039 wp_enqueue_style( SRFM_SLUG . $asset_handle . '-font', 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap', [], SRFM_VER );
1040
1041 $script_asset_path = SRFM_DIR . 'assets/build/dashboard.asset.php';
1042 $script_info = file_exists( $script_asset_path )
1043 ? include $script_asset_path
1044 : [
1045 'dependencies' => [],
1046 'version' => SRFM_VER,
1047 ];
1048 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/dashboard.js', $script_info['dependencies'], SRFM_VER, true );
1049
1050 wp_localize_script( SRFM_SLUG . $asset_handle, 'scIcons', [ 'path' => SRFM_URL . 'assets/build/icon-assets' ] );
1051
1052 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1053
1054 if ( class_exists( 'SRFM_PRO\Admin\Licensing' ) ) {
1055 $license_active = \SRFM_PRO\Admin\Licensing::is_license_active();
1056 $localization_data['is_license_active'] = $license_active;
1057
1058 // Updating current licensing status.
1059 $srfm_pro_license_status = get_option( 'srfm_pro_license_status', '' );
1060 $current_license_status = $license_active ? 'licensed' : 'unlicensed';
1061 if ( $current_license_status !== $srfm_pro_license_status ) {
1062 update_option( 'srfm_pro_license_status', $current_license_status );
1063 }
1064 }
1065
1066 $localization_data['security_settings_url'] = admin_url( '/admin.php?page=sureforms_form_settings&tab=security-settings&subpage=recaptcha' );
1067 $localization_data['integration_settings_url'] = admin_url( '/admin.php?page=sureforms_form_settings&tab=integration-settings' );
1068 wp_localize_script(
1069 SRFM_SLUG . $asset_handle,
1070 SRFM_SLUG . '_admin',
1071 apply_filters(
1072 SRFM_SLUG . '_admin_filter',
1073 $localization_data
1074 )
1075 );
1076 wp_enqueue_style( SRFM_SLUG . '-dashboard', SRFM_URL . 'assets/build/dashboard.css', [], SRFM_VER, 'all' );
1077 }
1078
1079 if ( $is_screen_sureforms_form_settings || $is_screen_sureforms_forms ) {
1080 wp_enqueue_style( SRFM_SLUG . '-settings', $css_uri . 'backend/settings' . $file_prefix . $rtl . '.css', [], SRFM_VER );
1081 }
1082
1083 // Enqueue styles for the entries page.
1084 if ( $is_screen_sureforms_entries ) {
1085 $asset_handle = '-entries';
1086 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/entries.js', $script_info['dependencies'], SRFM_VER, true );
1087
1088 wp_localize_script(
1089 SRFM_SLUG . $asset_handle,
1090 SRFM_SLUG . '_admin',
1091 apply_filters(
1092 SRFM_SLUG . '_admin_filter',
1093 $localization_data
1094 )
1095 );
1096 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1097 }
1098
1099 // Enqueue scripts for the learn page.
1100 if ( $is_screen_sureforms_learn ) {
1101 $asset_handle = '-learn';
1102 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/learn.js', $script_info['dependencies'], SRFM_VER, true );
1103
1104 wp_localize_script(
1105 SRFM_SLUG . $asset_handle,
1106 SRFM_SLUG . '_admin',
1107 apply_filters(
1108 SRFM_SLUG . '_admin_filter',
1109 $localization_data
1110 )
1111 );
1112 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1113 }
1114
1115 // Enqueue scripts for the forms page.
1116 if ( $is_screen_sureforms_forms ) {
1117 $asset_handle = '-forms';
1118
1119 $script_asset_path = SRFM_DIR . 'assets/build/forms.asset.php';
1120 $script_info = file_exists( $script_asset_path )
1121 ? include $script_asset_path
1122 : [
1123 'dependencies' => [],
1124 'version' => SRFM_VER,
1125 ];
1126
1127 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/forms.js', $script_info['dependencies'], SRFM_VER, true );
1128 wp_localize_script(
1129 SRFM_SLUG . $asset_handle,
1130 SRFM_SLUG . '_admin',
1131 apply_filters(
1132 SRFM_SLUG . '_admin_filter',
1133 $localization_data
1134 )
1135 );
1136 wp_enqueue_style( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/forms.css', [], SRFM_VER, 'all' );
1137
1138 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1139 }
1140
1141 // Enqueue scripts for the SureMail promotional page.
1142 $is_screen_sureforms_smtp = Helper::validate_request_context( 'sureforms_smtp', 'page' );
1143 if ( $is_screen_sureforms_smtp ) {
1144 $asset_handle = 'suremail';
1145
1146 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1147 $script_info = file_exists( $script_asset_path )
1148 ? include $script_asset_path
1149 : [
1150 'dependencies' => [],
1151 'version' => SRFM_VER,
1152 ];
1153
1154 wp_enqueue_script( SRFM_SLUG . '-suremail', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1155 wp_enqueue_style( SRFM_SLUG . '-suremail', SRFM_URL . 'assets/build/suremail.css', [], SRFM_VER, 'all' );
1156
1157 // Localize script for SureMail page.
1158 $suremail_localization_data = [
1159 'ajax_url' => admin_url( 'admin-ajax.php' ),
1160 'admin_url' => admin_url(),
1161 'suremail_url' => 'https://sureforms.com/suremail/',
1162 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
1163 'sfPluginManagerNonce' => wp_create_nonce( 'sf_plugin_manager_nonce' ),
1164 'suremail_status' => file_exists( WP_PLUGIN_DIR . '/suremails/suremails.php' )
1165 ? ( is_plugin_active( 'suremails/suremails.php' ) ? 'active' : 'installed' )
1166 : 'not_installed',
1167 ];
1168
1169 wp_localize_script(
1170 SRFM_SLUG . '-suremail',
1171 SRFM_SLUG . '_admin',
1172 apply_filters(
1173 SRFM_SLUG . '_suremail_admin_filter',
1174 $suremail_localization_data
1175 )
1176 );
1177
1178 $script_translations_handlers[] = SRFM_SLUG . '-suremail';
1179 }
1180
1181 // Enqueue scripts for the Quiz empty state page (free users only).
1182 if ( $is_screen_quiz_empty_state && ! Helper::has_pro() ) {
1183 $asset_handle = 'quizEmptyState';
1184
1185 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1186 $script_info = file_exists( $script_asset_path )
1187 ? include $script_asset_path
1188 : [
1189 'dependencies' => [],
1190 'version' => SRFM_VER,
1191 ];
1192
1193 wp_enqueue_script( SRFM_SLUG . '-quiz-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1194 wp_enqueue_style( SRFM_SLUG . '-quiz-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1195
1196 $script_translations_handlers[] = SRFM_SLUG . '-quiz-empty-state';
1197 }
1198
1199 // Enqueue scripts for the Survey Reports empty state page (free users only).
1200 if ( $is_screen_survey_empty_state && ! Helper::has_pro() ) {
1201 $asset_handle = 'surveyEmptyState';
1202
1203 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1204 $script_info = file_exists( $script_asset_path )
1205 ? include $script_asset_path
1206 : [
1207 'dependencies' => [],
1208 'version' => SRFM_VER,
1209 ];
1210
1211 wp_enqueue_script( SRFM_SLUG . '-survey-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1212 wp_enqueue_style( SRFM_SLUG . '-survey-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1213
1214 $script_translations_handlers[] = SRFM_SLUG . '-survey-empty-state';
1215 }
1216
1217 // Enqueue scripts for the Partial Entries empty state page (free users only).
1218 if ( $is_screen_partial_entries_empty_state && ! Helper::has_pro() ) {
1219 $asset_handle = 'partialEntriesEmptyState';
1220
1221 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1222 $script_info = file_exists( $script_asset_path )
1223 ? include $script_asset_path
1224 : [
1225 'dependencies' => [],
1226 'version' => SRFM_VER,
1227 ];
1228
1229 wp_enqueue_script( SRFM_SLUG . '-partial-entries-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1230 wp_enqueue_style( SRFM_SLUG . '-partial-entries-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1231
1232 $script_translations_handlers[] = SRFM_SLUG . '-partial-entries-empty-state';
1233 }
1234
1235 // Admin Submenu Styles.
1236 wp_enqueue_style( SRFM_SLUG . '-admin', $css_uri . 'backend/admin' . $file_prefix . $rtl . '.css', [], SRFM_VER );
1237
1238 if ( $is_screen_sureforms_form_settings ) {
1239 $asset_handle = 'settings';
1240
1241 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1242 $script_info = file_exists( $script_asset_path )
1243 ? include $script_asset_path
1244 : [
1245 'dependencies' => [],
1246 'version' => SRFM_VER,
1247 ];
1248
1249 wp_enqueue_script( SRFM_SLUG . '-settings', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1250 wp_localize_script(
1251 SRFM_SLUG . '-settings',
1252 SRFM_SLUG . '_admin',
1253 apply_filters(
1254 SRFM_SLUG . '_admin_filter',
1255 $localization_data
1256 )
1257 );
1258
1259 // Enqueue Tailwind and Quill editor styles for the settings page.
1260 wp_enqueue_style( SRFM_SLUG . '-settings-build', SRFM_URL . 'assets/build/settings.css', [], SRFM_VER, 'all' );
1261 wp_enqueue_style( SRFM_SLUG . '-reactQuill', SRFM_URL . 'assets/css/minified/deps/quill/quill.snow.css', [], SRFM_VER );
1262
1263 $script_translations_handlers[] = SRFM_SLUG . '-settings';
1264 }
1265
1266 if ( $is_screen_add_new_form ) {
1267 wp_enqueue_style( SRFM_SLUG . '-template-picker', $css_uri . 'template-picker' . $file_prefix . $rtl . '.css', [], SRFM_VER );
1268
1269 $sureforms_admin = 'templatePicker';
1270
1271 $script_asset_path = SRFM_DIR . 'assets/build/' . $sureforms_admin . '.asset.php';
1272 $script_info = file_exists( $script_asset_path )
1273 ? include $script_asset_path
1274 : [
1275 'dependencies' => [],
1276 'version' => SRFM_VER,
1277 ];
1278 wp_enqueue_script( SRFM_SLUG . '-template-picker', SRFM_URL . 'assets/build/' . $sureforms_admin . '.js', $script_info['dependencies'], SRFM_VER, true );
1279
1280 wp_localize_script(
1281 SRFM_SLUG . '-template-picker',
1282 SRFM_SLUG . '_admin',
1283 [
1284 'site_url' => get_site_url(),
1285 'plugin_url' => SRFM_URL,
1286 'admin_url' => admin_url( 'admin.php' ),
1287 'new_template_picker_base_url' => admin_url( 'post-new.php?post_type=sureforms_form' ),
1288 'capability' => Helper::current_user_can(),
1289 'template_picker_nonce' => Helper::current_user_can() ? wp_create_nonce( 'wp_rest' ) : '',
1290 'is_pro_active' => Helper::has_pro(),
1291 'srfm_ai_usage_details' => AI_Helper::get_current_usage_details(),
1292 'is_pro_license_active' => AI_Helper::is_pro_license_active(),
1293 'srfm_ai_auth_user_email' => get_option( 'srfm_ai_auth_user_email' ),
1294 'pricing_page_url' => Helper::get_sureforms_website_url( 'pricing' ),
1295 'licensing_nonce' => wp_create_nonce( 'srfm_pro_licensing_nonce' ),
1296 ]
1297 );
1298
1299 $script_translations_handlers[] = SRFM_SLUG . '-template-picker';
1300 }
1301 // Quick action sidebar.
1302 $default_allowed_quick_sidebar_blocks = apply_filters(
1303 'srfm_quick_sidebar_allowed_blocks',
1304 [
1305 'srfm/input',
1306 'srfm/email',
1307 'srfm/textarea',
1308 'srfm/checkbox',
1309 'srfm/number',
1310 'srfm/inline-button',
1311 'srfm/advanced-heading',
1312 'srfm/payment',
1313 ]
1314 );
1315 if ( ! is_array( $default_allowed_quick_sidebar_blocks ) ) {
1316 $default_allowed_quick_sidebar_blocks = [];
1317 }
1318
1319 $srfm_enable_quick_action_sidebar = get_option( 'srfm_enable_quick_action_sidebar' );
1320 if ( ! $srfm_enable_quick_action_sidebar ) {
1321 $srfm_enable_quick_action_sidebar = 'disabled';
1322 }
1323 $quick_sidebar_allowed_blocks = get_option( 'srfm_quick_sidebar_allowed_blocks' );
1324 $quick_sidebar_allowed_blocks = ! empty( $quick_sidebar_allowed_blocks ) && is_array( $quick_sidebar_allowed_blocks ) ? $quick_sidebar_allowed_blocks : $default_allowed_quick_sidebar_blocks;
1325 $srfm_ajax_nonce = wp_create_nonce( 'srfm_ajax_nonce' );
1326
1327 if ( Helper::is_sureforms_admin_page() ) {
1328 wp_enqueue_script( SRFM_SLUG . '-quick-action-siderbar', SRFM_URL . 'assets/build/quickActionSidebar.js', [], SRFM_VER, true );
1329 wp_localize_script(
1330 SRFM_SLUG . '-quick-action-siderbar',
1331 SRFM_SLUG . '_quick_sidebar_blocks',
1332 [
1333 'allowed_blocks' => $quick_sidebar_allowed_blocks,
1334 'srfm_enable_quick_action_sidebar' => $srfm_enable_quick_action_sidebar,
1335 'srfm_ajax_nonce' => $srfm_ajax_nonce,
1336 'srfm_ajax_url' => admin_url( 'admin-ajax.php' ),
1337 ]
1338 );
1339
1340 $script_translations_handlers[] = SRFM_SLUG . '-quick-action-siderbar';
1341 }
1342
1343 /**
1344 * Enqueuing SureTriggers Integration script.
1345 * This script loads suretriggers iframe in Intergations tab.
1346 */
1347 if ( $is_post_type_sureforms_form ) {
1348 wp_enqueue_script( SRFM_SLUG . '-suretriggers-integration', SRFM_SURETRIGGERS_INTEGRATION_BASE_URL . 'js/v2/embed.js', [], SRFM_VER, true );
1349 }
1350
1351 // Check $script_translations_handlers is not empty before calling the function.
1352 if ( ! empty( $script_translations_handlers ) ) {
1353 // Remove duplicates values from the array.
1354 $script_translations_handlers = array_unique( $script_translations_handlers );
1355
1356 foreach ( $script_translations_handlers as $script_handle ) {
1357 Helper::register_script_translations( $script_handle );
1358 }
1359 }
1360 }
1361
1362 /**
1363 * Form Template Picker Admin Body Classes
1364 * WordPress sometimes translates class names in the admin body tag, which can result in
1365 * incorrect or missing class names when rendering the admin pages. This function ensures
1366 * that essential class names are manually added to the body tag to maintain proper functionality.
1367 *
1368 * @since 0.0.1
1369 * @param string $classes Space separated class string.
1370 */
1371 public function admin_template_picker_body_class( $classes = '' ) {
1372 // Define an associative array of class names and their corresponding conditions.
1373 // Each condition checks whether a specific request context matches.
1374 $srfm_classes = [
1375 'sureforms_page_sureforms_entries' => Helper::validate_request_context( SRFM_ENTRIES, 'page' ),
1376 'sureforms_page_sureforms_form_settings' => Helper::validate_request_context( 'sureforms_form_settings', 'page' ),
1377 'srfm-template-picker' => Helper::validate_request_context( 'add-new-form', 'page' ),
1378 ];
1379
1380 $add_srfm_classes = '';
1381
1382 // Loop through the defined classes and conditions.
1383 foreach ( $srfm_classes as $class => $condition ) {
1384 // Check if the condition evaluates to true.
1385 if ( $condition ) {
1386 // Append the class to the existing classes string, followed by a space.
1387 $add_srfm_classes .= empty( $add_srfm_classes ) ? $class : ' ' . $class;
1388 }
1389 }
1390
1391 // Append the new classes to the existing classes string.
1392 if ( ! empty( $add_srfm_classes ) ) {
1393 $classes .= ' ' . $add_srfm_classes;
1394 }
1395
1396 // Return the updated list of classes.
1397 return $classes;
1398 }
1399
1400 /**
1401 * Disable spectra's quick action bar in sureforms CPT.
1402 *
1403 * @param string $status current status of the quick action bar.
1404 * @since 0.0.2
1405 * @return string
1406 */
1407 public function restrict_spectra_quick_action_bar( $status ) {
1408 $screen = get_current_screen();
1409 if ( 'disabled' !== $status && isset( $screen->id ) && 'sureforms_form' === $screen->id ) {
1410 $status = 'disabled';
1411 }
1412
1413 return $status;
1414 }
1415
1416 /**
1417 * Register Pro compatibility notices early for React pages.
1418 *
1419 * This method runs on admin_init (priority 5) to ensure notices are
1420 * registered BEFORE admin_enqueue_scripts, so they're available when
1421 * wp_localize_script runs.
1422 *
1423 * Hooked - admin_init (priority 5)
1424 *
1425 * @return void
1426 * @since 2.5.0
1427 */
1428 public function register_pro_compatibility_notices() {
1429 // Early exit if Pro is not active, user lacks permissions, or Notice_Manager is unavailable.
1430 if ( ! Helper::has_pro() || ! Helper::current_user_can() || ! class_exists( 'SRFM\Admin\Notice_Manager' ) ) {
1431 return;
1432 }
1433
1434 // Register version outdated notice for React pages.
1435 if ( ! version_compare( SRFM_PRO_VER, SRFM_PRO_RECOMMENDED_VER, '>=' ) ) {
1436 $pro_plugin_name = defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'SureForms Pro';
1437 $react_outdated_message = sprintf(
1438 // translators: %1$s: SureForms version, %2$s: SureForms Pro Plugin Name, %3$s: SureForms Pro Version.
1439 esc_html__( 'SureForms %1$s requires minimum %2$s %3$s to work properly. Please update to the latest version.', 'sureforms' ),
1440 esc_html( SRFM_VER ),
1441 esc_html( $pro_plugin_name ),
1442 esc_html( SRFM_PRO_RECOMMENDED_VER )
1443 );
1444
1445 \SRFM\Admin\Notice_Manager::register_notice(
1446 [
1447 'id' => 'sureforms-pro-version-outdated',
1448 'variant' => 'warning',
1449 'message' => $react_outdated_message,
1450 'actions' => [
1451 [
1452 'label' => esc_html__( 'Update Now', 'sureforms' ),
1453 'url' => admin_url( 'update-core.php' ),
1454 'variant' => 'primary',
1455 ],
1456 ],
1457 'pages' => [ 'all' ],
1458 ]
1459 );
1460 }
1461 }
1462
1463 /**
1464 * Admin Notice Callback if sureforms pro is out of date.
1465 *
1466 * Hooked - admin_notices
1467 *
1468 * @return void
1469 * @since 1.0.4
1470 */
1471 public function srfm_pro_version_compatibility() {
1472 if ( ! Helper::has_pro() ) {
1473 return;
1474 }
1475
1476 if ( empty( get_current_screen() ) ) {
1477 return;
1478 }
1479
1480 if ( ! Helper::current_user_can() ) {
1481 return;
1482 }
1483
1484 $srfm_pro_license_status = get_option( 'srfm_pro_license_status', '' );
1485 /**
1486 * If the license status is not set then get the license status and update the option accordingly.
1487 * This will be executed only once. Subsequently, the option status is updated by the licensing class on license activation or deactivation.
1488 */
1489 if ( empty( $srfm_pro_license_status ) && class_exists( 'SRFM_PRO\Admin\Licensing' ) ) {
1490 $srfm_pro_license_status = \SRFM_PRO\Admin\Licensing::is_license_active() ? 'licensed' : 'unlicensed';
1491 update_option( 'srfm_pro_license_status', $srfm_pro_license_status );
1492 }
1493
1494 $pro_plugin_name = defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'SureForms Pro';
1495 $message = '';
1496 $url = admin_url( 'admin.php?page=sureforms_form_settings&tab=account-settings' );
1497 if ( 'unlicensed' === $srfm_pro_license_status ) {
1498 ob_start();
1499 ?>
1500 <p>
1501 <?php
1502 printf(
1503 // translators: %1$s: Opening anchor tag with URL, %2$s: Closing anchor tag, %3$s: SureForms Pro Plugin Name.
1504 esc_html__( 'Please %1$sactivate%2$s your copy of %3$s to get new features, access support, receive update notifications, and more.', 'sureforms' ),
1505 '<a href="' . esc_url( $url ) . '">',
1506 '</a>',
1507 '<i>' . esc_html( $pro_plugin_name ) . '</i>'
1508 );
1509 ?>
1510 </p>
1511 <?php
1512 $message = ob_get_clean();
1513 }
1514
1515 if ( ! version_compare( SRFM_PRO_VER, SRFM_PRO_RECOMMENDED_VER, '>=' ) ) {
1516 ob_start();
1517 ?>
1518 <p>
1519 <?php
1520 printf(
1521 // translators: %1$s: SureForms version, %2$s: SureForms Pro Plugin Name, %3$s: SureForms Pro Version, %4$s: Anchor tag open, %5$s: Closing anchor tag.
1522 esc_html__( 'SureForms %1$s requires minimum %2$s %3$s to work properly. Please update to the latest version from %4$shere%5$s.', 'sureforms' ),
1523 esc_html( SRFM_VER ),
1524 esc_html( $pro_plugin_name ),
1525 esc_html( SRFM_PRO_RECOMMENDED_VER ),
1526 '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">',
1527 '</a>'
1528 );
1529 ?>
1530 </p>
1531 <?php
1532 $message .= ob_get_clean();
1533 }
1534
1535 if ( ! empty( $message ) ) {
1536 // Phpcs ignore comment is required as $message variable is already escaped.
1537 ?>
1538 <div class="notice notice-warning"><?php echo wp_kses_post( $message ); ?></div>
1539 <?php
1540 }
1541 }
1542
1543 /**
1544 * Display a notice to the user about providing a review.
1545 *
1546 * @since 2.5.2
1547 * @return void
1548 */
1549 public function display_srfm_rating_notice() {
1550 // Only show to admins.
1551 if ( ! Helper::current_user_can() ) {
1552 return;
1553 }
1554
1555 // Allow the notice to be disabled.
1556 if ( ! apply_filters( 'srfm_show_rating_notice', true ) ) {
1557 return;
1558 }
1559
1560 Astra_Notices::add_notice(
1561 [
1562 'id' => 'srfm-plugin-review-notice',
1563 'type' => '',
1564 'message' => $this->build_notice_markup(
1565 esc_html__( 'Amazing! SureForms is powering your forms and submissions - let\'s keep growing together!', 'sureforms' ),
1566 esc_html__( 'If SureForms has been helpful, would you mind taking a moment to leave a 5-star review on WordPress.org?', 'sureforms' ),
1567 esc_url( 'https://wordpress.org/support/plugin/sureforms/reviews/?filter=5#new-post' ),
1568 esc_html__( 'Rate SureForms', 'sureforms' ),
1569 esc_html__( 'Maybe later', 'sureforms' ),
1570 esc_html__( 'I already did', 'sureforms' ),
1571 WEEK_IN_SECONDS,
1572 true
1573 ),
1574 'repeat-notice-after' => WEEK_IN_SECONDS,
1575 'show_if' => $this->maybe_display_rating_notice(),
1576 'display-with-other-notices' => true,
1577 ]
1578 );
1579
1580 add_action( 'astra_notice_after_markup_srfm-plugin-review-notice', [ $this, 'enqueue_notice_response_script' ] );
1581 }
1582
1583 /**
1584 * Display a "Getting Started" admin notice for new users who haven't yet
1585 * reached the rating-notice milestone (3+ forms or 3+ entries).
1586 *
1587 * The Astra Notices library handles the 7-day delay via the
1588 * `display-notice-after` parameter.
1589 *
1590 * @since 2.5.2
1591 * @return void
1592 */
1593 public function display_srfm_getting_started_notice() {
1594 // Only show to admins.
1595 if ( ! Helper::current_user_can() ) {
1596 return;
1597 }
1598
1599 // Allow the notice to be disabled programmatically.
1600 if ( ! apply_filters( 'srfm_show_getting_started_notice', true ) ) {
1601 return;
1602 }
1603
1604 Astra_Notices::add_notice(
1605 [
1606 'id' => 'srfm-getting-started-notice',
1607 'type' => '',
1608 'message' => $this->build_notice_markup(
1609 esc_html__( 'SureForms is ready to power your forms — explore what\'s possible!', 'sureforms' ),
1610 esc_html__( 'Manage your forms, track submissions, and discover features like AI Form Builder, payment integrations, and more from the SureForms dashboard.', 'sureforms' ),
1611 esc_url( admin_url( 'admin.php?page=sureforms_menu' ) ),
1612 esc_html__( 'Go to Dashboard', 'sureforms' ),
1613 esc_html__( 'Maybe later', 'sureforms' ),
1614 esc_html__( 'I already know', 'sureforms' ),
1615 WEEK_IN_SECONDS
1616 ),
1617 'repeat-notice-after' => WEEK_IN_SECONDS,
1618 'show_if' => ! $this->maybe_display_rating_notice(),
1619 'display-notice-after' => WEEK_IN_SECONDS,
1620 'display-with-other-notices' => true,
1621 ]
1622 );
1623
1624 add_action( 'astra_notice_after_markup_srfm-getting-started-notice', [ $this, 'enqueue_notice_response_script' ] );
1625 }
1626
1627 /**
1628 * Enqueue the notice response analytics script.
1629 *
1630 * Called via the astra_notice_after_markup_{id} hook so the script
1631 * only loads when a SureForms notice is actually rendered.
1632 *
1633 * @since 2.5.2
1634 * @return void
1635 */
1636 public function enqueue_notice_response_script() {
1637 if ( wp_script_is( 'srfm-notice-response', 'enqueued' ) ) {
1638 return;
1639 }
1640
1641 wp_enqueue_script(
1642 'srfm-notice-response',
1643 SRFM_URL . 'admin/assets/js/notice-response.js',
1644 [],
1645 SRFM_VER,
1646 true
1647 );
1648
1649 wp_localize_script(
1650 'srfm-notice-response',
1651 'srfmNoticeResponse',
1652 [
1653 'ajaxurl' => admin_url( 'admin-ajax.php' ),
1654 'nonce' => wp_create_nonce( 'srfm_notice_response' ),
1655 ]
1656 );
1657 }
1658
1659 /**
1660 * Handle the notice response AJAX request.
1661 *
1662 * Validates the request and records the analytics event
1663 * for the notice button that was clicked.
1664 *
1665 * @since 2.5.2
1666 * @return void
1667 */
1668 public function handle_notice_response() {
1669 if ( ! check_ajax_referer( 'srfm_notice_response', 'nonce', false ) ) {
1670 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1671 }
1672
1673 if ( ! Helper::current_user_can() ) {
1674 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1675 }
1676
1677 $notice_id = isset( $_POST['notice_id'] ) ? sanitize_text_field( wp_unslash( $_POST['notice_id'] ) ) : '';
1678 $button = isset( $_POST['button'] ) ? sanitize_text_field( wp_unslash( $_POST['button'] ) ) : '';
1679
1680 $valid = [
1681 'srfm-getting-started-notice' => [
1682 'go_to_dashboard' => 'getting_started_notice_cta',
1683 'maybe_later' => 'getting_started_notice_snooze',
1684 'dismissed' => 'getting_started_notice_dismiss',
1685 ],
1686 'srfm-plugin-review-notice' => [
1687 'rate_sureforms' => 'rating_notice_cta',
1688 'maybe_later' => 'rating_notice_snooze',
1689 'dismissed' => 'rating_notice_dismiss',
1690 ],
1691 ];
1692
1693 if ( ! isset( $valid[ $notice_id ][ $button ] ) ) {
1694 wp_send_json_error( [ 'message' => __( 'Invalid parameters.', 'sureforms' ) ], 400 );
1695 }
1696
1697 $event_name = $valid[ $notice_id ][ $button ];
1698 Analytics::events()->track( $event_name, $button );
1699
1700 wp_send_json_success();
1701 }
1702
1703 /**
1704 * Disables the capabilities for WPForms to avoid conflicts when enqueueing
1705 * scripts and styles for WPForms.
1706 *
1707 * This function is intended to prevent any potential conflicts that may arise
1708 * when WPForms scripts and styles are enqueued. By disabling certain capabilities,
1709 * it ensures that WPForms does not interfere with other functionalities.
1710 *
1711 * @param bool $user_can A boolean indicating whether the user has the capability.
1712 * @return bool Returns true if the capabilities are successfully disabled, false otherwise.
1713 * @since 1.4.2
1714 */
1715 public function disable_wpforms_capabilities( $user_can ) {
1716 // Note: Nonce verification is intentionally omitted here as no database operations are performed.
1717 // The values of the $_REQUEST variables are strictly validated, ensuring security without the need for nonce verification.
1718
1719 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1720 $post_id = ! empty( $_REQUEST['post'] ) && ! empty( $_REQUEST['action'] ) ? absint( $_REQUEST['post'] ) : 0;
1721
1722 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1723 $post_type = $post_id ? get_post_type( $post_id ) : sanitize_text_field( wp_unslash( $_REQUEST['post_type'] ?? '' ) );
1724 return SRFM_FORMS_POST_TYPE === $post_type ? false : $user_can;
1725 }
1726
1727 /**
1728 * Enqueueus the admin pointer script and styles.
1729 *
1730 * @return void
1731 * @since 1.8.0
1732 */
1733 public function enqueue_admin_pointer() {
1734 if ( ! $this->is_admin_pointer_visible() ) {
1735 return;
1736 }
1737 wp_enqueue_style( 'wp-pointer' );
1738 wp_enqueue_script( 'wp-pointer' );
1739 wp_enqueue_script(
1740 'sureforms-admin-pointer',
1741 plugins_url( 'admin/assets/js/sureforms-pointer.js', SRFM_FILE ),
1742 [ 'wp-pointer', 'jquery' ],
1743 SRFM_VER,
1744 true
1745 );
1746 wp_localize_script(
1747 'sureforms-admin-pointer',
1748 'sureformsPointerData',
1749 [
1750 'ajaxurl' => admin_url( 'admin-ajax.php' ),
1751 'pointer_nonce' => wp_create_nonce( 'sureforms_pointer_action' ),
1752 ]
1753 );
1754 }
1755
1756 /**
1757 * Ajax handler for pointer popup visibility.
1758 *
1759 * @return void
1760 * @since 1.8.0
1761 */
1762 public function pointer_should_show() {
1763 // Security: Check user capability.
1764 if ( ! Helper::current_user_can() ) {
1765 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1766 }
1767 // Security: Nonce check.
1768 if ( empty( $_POST['pointer_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pointer_nonce'] ) ), 'sureforms_pointer_action' ) ) {
1769 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1770 }
1771
1772 $content_markup = sprintf(
1773 /* translators: 1: opening span, 2: opening strong (inline), 3: closing strong, 4: closing span, 5: opening strong (block), 6: closing strong */
1774 __( '%1$sGet started by %2$sbuilding your first form%3$s.%4$s%5$sExperience the power of our intuitive AI Form Builder%6$s', 'sureforms' ),
1775 '<span>',
1776 '<strong>',
1777 '</strong>',
1778 '</span><br/>',
1779 '<strong style="font-size:1.1em;">',
1780 '</strong>'
1781 );
1782 wp_send_json(
1783 [
1784 'show' => true,
1785 'title' => esc_html( __( 'SureForms is waiting for you!', 'sureforms' ) ),
1786 'content' => wp_kses_post( $content_markup ),
1787 'button_text' => esc_html( __( 'Build My First Form', 'sureforms' ) ),
1788 'dismiss' => esc_html( __( 'Dismiss', 'sureforms' ) ),
1789 'button_url' => admin_url( 'admin.php?page=add-new-form' ),
1790 ]
1791 );
1792 }
1793
1794 /**
1795 * Ajax callback for pointer popup dismissed action.
1796 *
1797 * @return void
1798 * @since 1.8.0
1799 */
1800 public function pointer_dismissed() {
1801 // Security: Check user capability.
1802 if ( ! Helper::current_user_can() ) {
1803 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1804 }
1805 // Security: Nonce check.
1806 if ( empty( $_POST['pointer_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pointer_nonce'] ) ), 'sureforms_pointer_action' ) ) {
1807 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1808 }
1809 // Use Helper to update srfm_options key.
1810 Helper::update_srfm_option( 'pointer_popup_dismissed', time() );
1811
1812 wp_send_json_success();
1813 }
1814
1815 /**
1816 * Ajax pointer accepted CTA callback.
1817 *
1818 * @return void
1819 * @since 1.8.0
1820 */
1821 public function pointer_accepted_cta() {
1822 // Security: Check user capability.
1823 if ( ! Helper::current_user_can() ) {
1824 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1825 }
1826 // Security: Nonce check.
1827 if ( empty( $_POST['pointer_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pointer_nonce'] ) ), 'sureforms_pointer_action' ) ) {
1828 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1829 }
1830 // Use Helper to update srfm_options key.
1831 Helper::update_srfm_option( 'pointer_popup_accepted', time() );
1832
1833 wp_send_json_success();
1834 }
1835
1836 /**
1837 * Maybe register the dashboard widget based on entries.
1838 *
1839 * @return void
1840 * @since 1.9.1
1841 */
1842 public function maybe_register_dashboard_widget() {
1843
1844 // Only for users with manage_options capability.
1845 if ( ! Helper::current_user_can() ) {
1846 return;
1847 }
1848
1849 // Quick check if there are any entries in the last 7 days.
1850 $seven_days_ago = strtotime( '-7 days' );
1851 $total_entries = Entries::get_entries_count_after( $seven_days_ago );
1852
1853 // Only add the dashboard setup hook if there are entries.
1854 if ( $total_entries > 0 ) {
1855 // Get forms with entries (limit 4 for dashboard widget).
1856 $this->dashboard_widget_data = Helper::get_forms_with_entry_counts( $seven_days_ago, 4 );
1857
1858 // Only show dashboard widget if there are forms with entries.
1859 if ( ! empty( $this->dashboard_widget_data ) ) {
1860 add_action( 'wp_dashboard_setup', [ $this, 'register_dashboard_widget' ] );
1861 }
1862 }
1863 }
1864
1865 /**
1866 * Register the dashboard widget.
1867 *
1868 * @return void
1869 * @since 1.9.1
1870 */
1871 public function register_dashboard_widget() {
1872 // Add the widget with high priority to position it at the top.
1873 wp_add_dashboard_widget(
1874 'sureforms_recent_entries',
1875 __( 'SureForms', 'sureforms' ),
1876 [ $this, 'render_dashboard_widget' ],
1877 null,
1878 null,
1879 'normal',
1880 'high'
1881 );
1882 }
1883
1884 /**
1885 * Render the dashboard widget content.
1886 *
1887 * @return void
1888 * @since 1.9.1
1889 */
1890 public function render_dashboard_widget() {
1891 // Use the pre-fetched data to avoid duplicate queries.
1892 $entries_data = $this->dashboard_widget_data;
1893
1894 // Display the widget content.
1895 ?>
1896 <div class="srfm-dashboard-widget">
1897 <div class="srfm-widget-header">
1898 <h3 class="srfm-widget-title">
1899 <?php esc_html_e( 'Recent Entries', 'sureforms' ); ?>
1900 <span class="srfm-widget-subtitle"><?php esc_html_e( '( Last 7 days )', 'sureforms' ); ?></span>
1901 </h3>
1902 <a href="<?php echo esc_url( admin_url( 'admin.php?page=sureforms_entries' ) ); ?>" class="srfm-widget-view-link">
1903 <?php esc_html_e( 'View', 'sureforms' ); ?>
1904 </a>
1905 </div>
1906
1907 <div class="srfm-table-wrapper">
1908 <table class="srfm-entries-table">
1909 <thead>
1910 <tr>
1911 <th><?php esc_html_e( 'Form Name', 'sureforms' ); ?></th>
1912 <th><?php esc_html_e( 'Entries', 'sureforms' ); ?></th>
1913 </tr>
1914 </thead>
1915 <tbody>
1916 <?php foreach ( $entries_data as $form_data ) { ?>
1917 <tr>
1918 <td class="form-name"><?php echo esc_html( $form_data['title'] ); ?></td>
1919 <td class="entry-count"><?php echo esc_html( $form_data['count'] ); ?></td>
1920 </tr>
1921 <?php } ?>
1922 </tbody>
1923 </table>
1924 </div>
1925
1926 <?php
1927 // Render footer if applicable.
1928 $this->render_dashboard_widget_footer( $entries_data );
1929 ?>
1930 </div>
1931 <?php
1932 }
1933
1934 /**
1935 * Build the shared HTML markup for admin notices.
1936 *
1937 * @since 2.5.2
1938 *
1939 * All text parameters must be pre-escaped by the caller (e.g. via esc_html__()).
1940 * URL parameters must be pre-escaped via esc_url().
1941 *
1942 * @param string $heading The notice heading text (pre-escaped).
1943 * @param string $message The notice body text (pre-escaped).
1944 * @param string $cta_url The primary CTA URL (pre-escaped).
1945 * @param string $cta_text The primary CTA button text (pre-escaped).
1946 * @param string $snooze_text The snooze button text (pre-escaped).
1947 * @param string $dismiss_text The dismiss button text (pre-escaped).
1948 * @param int $snooze_duration Snooze duration in seconds for the data-repeat-notice-after attribute.
1949 * @param bool $external_cta Whether the CTA opens in a new tab and also dismisses the notice
1950 * via the astra-notice-close class. Default false.
1951 * @return string The notice HTML markup.
1952 */
1953 private function build_notice_markup( $heading, $message, $cta_url, $cta_text, $snooze_text, $dismiss_text, $snooze_duration, $external_cta = false ) {
1954 $image_path = esc_url( SRFM_URL . 'admin/assets/sureforms-logo.png' );
1955 $cta_class = $external_cta ? 'astra-notice-close button-primary' : 'button-primary';
1956 $cta_attrs = $external_cta ? ' target="_blank" rel="noopener noreferrer"' : '';
1957
1958 return sprintf(
1959 '<div class="notice-image">
1960 <img src="%1$s" class="custom-logo" alt="SureForms" itemprop="logo">
1961 </div>
1962 <div class="notice-content">
1963 <div class="notice-heading">
1964 %2$s
1965 </div>
1966 %3$s<br />
1967 <div class="astra-review-notice-container">
1968 <a href="%4$s" class="%5$s"%6$s>
1969 %7$s
1970 </a>
1971 <span class="dashicons dashicons-clock" aria-hidden="true"></span>
1972 <a href="#" data-repeat-notice-after="%8$s" class="astra-notice-close">
1973 %9$s
1974 </a>
1975 <span class="dashicons dashicons-smiley" aria-hidden="true"></span>
1976 <a href="#" class="astra-notice-close">
1977 %10$s
1978 </a>
1979 </div>
1980 </div>',
1981 $image_path,
1982 $heading,
1983 $message,
1984 $cta_url,
1985 esc_attr( $cta_class ),
1986 $cta_attrs,
1987 $cta_text,
1988 $snooze_duration,
1989 $snooze_text,
1990 $dismiss_text
1991 );
1992 }
1993
1994 /**
1995 * Callback for displaying the rating notice conditionally.
1996 *
1997 * Returns true if the user has 3 or more published forms or 3 or more form entries.
1998 *
1999 * @since 2.5.2
2000 * @return bool
2001 */
2002 private function maybe_display_rating_notice() {
2003 if ( null === $this->should_show_rating ) {
2004 $entries_count = Entries::get_total_entries_by_status( 'all' );
2005 $form_count = wp_count_posts( SRFM_FORMS_POST_TYPE );
2006 $this->should_show_rating = $entries_count >= self::RATING_NOTICE_THRESHOLD || Helper::get_integer_value( $form_count->publish ?? 0 ) >= self::RATING_NOTICE_THRESHOLD;
2007 }
2008
2009 return $this->should_show_rating;
2010 }
2011
2012 /**
2013 * Get random premium feature text.
2014 *
2015 * @return string Random feature text.
2016 * @since 1.9.1
2017 */
2018 private function get_random_premium_feature_text() {
2019 $features = [
2020 __( 'Use Conditional Logic to show only what matters', 'sureforms' ),
2021 __( 'Split your form into steps to keep it easy', 'sureforms' ),
2022 __( 'Let people upload files directly to your form', 'sureforms' ),
2023 __( 'Turn responses into downloadable PDFs automatically', 'sureforms' ),
2024 __( 'Let users sign with a simple signature field', 'sureforms' ),
2025 __( 'Connect your form to other tools using webhooks', 'sureforms' ),
2026 __( 'Use Conversational Forms for a chat-like experience', 'sureforms' ),
2027 __( 'Let users register or log in through your form', 'sureforms' ),
2028 __( 'Build forms that create WordPress user accounts', 'sureforms' ),
2029 __( 'Add calculations to auto-total scores or prices', 'sureforms' ),
2030 ];
2031
2032 // Get a random feature.
2033 $random_key = array_rand( $features );
2034 return $features[ $random_key ];
2035 }
2036
2037 /**
2038 * Render the dashboard widget footer for upsell.
2039 *
2040 * @param array $entries_data The entries data array.
2041 * @return void
2042 * @since 1.9.1
2043 */
2044 private function render_dashboard_widget_footer( $entries_data ) {
2045 // Only show footer if Pro is not active.
2046 if ( Helper::has_pro() ) {
2047 return;
2048 }
2049
2050 // Count total entries in last 7 days.
2051 $total_entries = 0;
2052 foreach ( $entries_data as $form_data ) {
2053 $total_entries += $form_data['count'];
2054 }
2055
2056 // Count total published forms.
2057 $published_forms_count = wp_count_posts( SRFM_FORMS_POST_TYPE )->publish;
2058
2059 // Show footer only if 3+ entries received OR 3+ forms published.
2060 if ( $total_entries >= 3 || $published_forms_count >= 3 ) {
2061 ?>
2062 <div class="srfm-widget-footer">
2063 <div class="srfm-upgrade-content">
2064 <svg class="srfm-logo-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2065 <rect width="20" height="20" fill="#D54407"/>
2066 <path d="M5.7139 4.2854H14.2853V7.1425H7.1424L5.7139 8.5711V7.1425V4.2854Z" fill="white"/>
2067 <path d="M5.7139 4.2854H14.2853V7.1425H7.1424L5.7139 8.5711V7.1425V4.2854Z" fill="white"/>
2068 <path d="M5.7148 8.5713H12.8577V11.4284H7.1434L5.7148 12.857V11.4284V8.5713Z" fill="white"/>
2069 <path d="M5.7148 8.5713H12.8577V11.4284H7.1434L5.7148 12.857V11.4284V8.5713Z" fill="white"/>
2070 <path d="M5.7148 12.8569H10.0006V15.7141H5.7148V12.8569Z" fill="white"/>
2071 <path d="M5.7148 12.8569H10.0006V15.7141H5.7148V12.8569Z" fill="white"/>
2072 </svg>
2073 <span><?php echo esc_html( $this->get_random_premium_feature_text() ); ?></span>
2074 </div>
2075 <?php
2076 $upgrade_url = Helper::get_sureforms_website_url( 'pricing', [ 'utm_medium' => 'dashboard-widget' ] );
2077 ?>
2078 <a href="<?php echo esc_url( $upgrade_url ); ?>" class="srfm-upgrade-link" target="_blank">
2079 <?php esc_html_e( 'Upgrade', 'sureforms' ); ?>
2080 </a>
2081 </div>
2082 <?php
2083 }
2084 }
2085
2086 /**
2087 * Determine if the admin pointer should be visible on this page.
2088 *
2089 * @since 1.8.0
2090 * @return bool
2091 */
2092 private function is_admin_pointer_visible() {
2093 global $pagenow;
2094 $allowed_pages = [ 'index.php', 'options-general.php' ];
2095
2096 // Do not show if pointer dismissed, accepted, or more than 1 form exists.
2097 if (
2098 ! empty( Helper::get_srfm_option( 'pointer_popup_dismissed' ) )
2099 || ! empty( Helper::get_srfm_option( 'pointer_popup_accepted' ) )
2100 || (int) ( wp_count_posts( SRFM_FORMS_POST_TYPE )->publish ?? 0 ) > 1
2101 ) {
2102 return false;
2103 }
2104
2105 if ( in_array( $pagenow, $allowed_pages, true ) ) {
2106 return true;
2107 }
2108
2109 return false;
2110 }
2111
2112 }
2113