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