PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.8.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.8.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 in SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz 2.8.0, at admin/admin.php

2,055 lines 68.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\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( 'Astra_Notices' ) ) {
24 require_once SRFM_DIR . 'inc/lib/astra-notices/class-astra-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 ];
947
948 $is_screen_sureforms_menu = Helper::validate_request_context( 'sureforms_menu', 'page' );
949 $is_screen_add_new_form = Helper::validate_request_context( 'add-new-form', 'page' );
950 $is_screen_sureforms_forms = Helper::validate_request_context( 'sureforms_forms', 'page' );
951 $is_screen_sureforms_form_settings = Helper::validate_request_context( 'sureforms_form_settings', 'page' );
952 $is_screen_sureforms_payments = Helper::validate_request_context( 'sureforms_payments', 'page' );
953 $is_screen_sureforms_entries = Helper::validate_request_context( SRFM_ENTRIES, 'page' );
954 $is_screen_sureforms_learn = Helper::validate_request_context( 'sureforms_learn', 'page' );
955 $is_screen_quiz_empty_state = Helper::validate_request_context( 'sureforms_quiz_entries', 'page' );
956 $is_screen_survey_empty_state = Helper::validate_request_context( 'sureforms_survey_reports', 'page' );
957 $is_post_type_sureforms_form = SRFM_FORMS_POST_TYPE === $current_screen->post_type;
958
959 /**
960 * Check if the current screen is the SureForms Menu and AI Auth Email is present then we will add user type as registered.
961 * Compatibility with existing UI code that checks for this condition.
962 */
963 if ( $is_screen_sureforms_menu ) {
964 // If email is stored send the user type as registered else non-registered.
965 $localization_data['srfm_ai_details'] = [
966 'type' => ! empty( get_option( 'srfm_ai_auth_user_email' ) ) ? 'registered' : 'non-registered',
967 ];
968 }
969
970 // Add the Quizzes and Survey Reports nav items when pro is not active.
971 if ( ! Helper::has_pro() ) {
972 $localization_data['additional_header_nav_items'][] = [
973 'slug' => 'sureforms_quiz_entries',
974 'text' => __( 'Quizzes', 'sureforms' ),
975 'link' => admin_url( 'admin.php?page=sureforms_quiz_entries' ),
976 ];
977 $localization_data['additional_header_nav_items'][] = [
978 'slug' => 'sureforms_survey_reports',
979 'text' => __( 'Survey Reports', 'sureforms' ),
980 'link' => admin_url( 'admin.php?page=sureforms_survey_reports' ),
981 ];
982 }
983
984 $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;
985
986 /**
987 * Filter to allow extending the SureForms dashboard screen check.
988 *
989 * @since 2.6.0
990 *
991 * @param bool $is_sureforms_screen Whether the current screen is a SureForms dashboard screen.
992 */
993 $is_sureforms_screen = apply_filters( 'srfm_is_dashboard_screen', $is_sureforms_screen );
994
995 if ( $is_sureforms_screen ) {
996 $asset_handle = '-dashboard';
997
998 wp_enqueue_style( SRFM_SLUG . $asset_handle . '-font', 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap', [], SRFM_VER );
999
1000 $script_asset_path = SRFM_DIR . 'assets/build/dashboard.asset.php';
1001 $script_info = file_exists( $script_asset_path )
1002 ? include $script_asset_path
1003 : [
1004 'dependencies' => [],
1005 'version' => SRFM_VER,
1006 ];
1007 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/dashboard.js', $script_info['dependencies'], SRFM_VER, true );
1008
1009 wp_localize_script( SRFM_SLUG . $asset_handle, 'scIcons', [ 'path' => SRFM_URL . 'assets/build/icon-assets' ] );
1010
1011 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1012
1013 if ( class_exists( 'SRFM_PRO\Admin\Licensing' ) ) {
1014 $license_active = \SRFM_PRO\Admin\Licensing::is_license_active();
1015 $localization_data['is_license_active'] = $license_active;
1016
1017 // Updating current licensing status.
1018 $srfm_pro_license_status = get_option( 'srfm_pro_license_status', '' );
1019 $current_license_status = $license_active ? 'licensed' : 'unlicensed';
1020 if ( $current_license_status !== $srfm_pro_license_status ) {
1021 update_option( 'srfm_pro_license_status', $current_license_status );
1022 }
1023 }
1024
1025 $localization_data['security_settings_url'] = admin_url( '/admin.php?page=sureforms_form_settings&tab=security-settings&subpage=recaptcha' );
1026 $localization_data['integration_settings_url'] = admin_url( '/admin.php?page=sureforms_form_settings&tab=integration-settings' );
1027 wp_localize_script(
1028 SRFM_SLUG . $asset_handle,
1029 SRFM_SLUG . '_admin',
1030 apply_filters(
1031 SRFM_SLUG . '_admin_filter',
1032 $localization_data
1033 )
1034 );
1035 wp_enqueue_style( SRFM_SLUG . '-dashboard', SRFM_URL . 'assets/build/dashboard.css', [], SRFM_VER, 'all' );
1036 }
1037
1038 if ( $is_screen_sureforms_form_settings || $is_screen_sureforms_forms ) {
1039 wp_enqueue_style( SRFM_SLUG . '-settings', $css_uri . 'backend/settings' . $file_prefix . $rtl . '.css', [], SRFM_VER );
1040 }
1041
1042 // Enqueue styles for the entries page.
1043 if ( $is_screen_sureforms_entries ) {
1044 $asset_handle = '-entries';
1045 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/entries.js', $script_info['dependencies'], SRFM_VER, true );
1046
1047 wp_localize_script(
1048 SRFM_SLUG . $asset_handle,
1049 SRFM_SLUG . '_admin',
1050 apply_filters(
1051 SRFM_SLUG . '_admin_filter',
1052 $localization_data
1053 )
1054 );
1055 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1056 }
1057
1058 // Enqueue scripts for the learn page.
1059 if ( $is_screen_sureforms_learn ) {
1060 $asset_handle = '-learn';
1061 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/learn.js', $script_info['dependencies'], SRFM_VER, true );
1062
1063 wp_localize_script(
1064 SRFM_SLUG . $asset_handle,
1065 SRFM_SLUG . '_admin',
1066 apply_filters(
1067 SRFM_SLUG . '_admin_filter',
1068 $localization_data
1069 )
1070 );
1071 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1072 }
1073
1074 // Enqueue scripts for the forms page.
1075 if ( $is_screen_sureforms_forms ) {
1076 $asset_handle = '-forms';
1077
1078 $script_asset_path = SRFM_DIR . 'assets/build/forms.asset.php';
1079 $script_info = file_exists( $script_asset_path )
1080 ? include $script_asset_path
1081 : [
1082 'dependencies' => [],
1083 'version' => SRFM_VER,
1084 ];
1085
1086 wp_enqueue_script( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/forms.js', $script_info['dependencies'], SRFM_VER, true );
1087 wp_localize_script(
1088 SRFM_SLUG . $asset_handle,
1089 SRFM_SLUG . '_admin',
1090 apply_filters(
1091 SRFM_SLUG . '_admin_filter',
1092 $localization_data
1093 )
1094 );
1095 wp_enqueue_style( SRFM_SLUG . $asset_handle, SRFM_URL . 'assets/build/forms.css', [], SRFM_VER, 'all' );
1096
1097 $script_translations_handlers[] = SRFM_SLUG . $asset_handle;
1098 }
1099
1100 // Enqueue scripts for the SureMail promotional page.
1101 $is_screen_sureforms_smtp = Helper::validate_request_context( 'sureforms_smtp', 'page' );
1102 if ( $is_screen_sureforms_smtp ) {
1103 $asset_handle = 'suremail';
1104
1105 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1106 $script_info = file_exists( $script_asset_path )
1107 ? include $script_asset_path
1108 : [
1109 'dependencies' => [],
1110 'version' => SRFM_VER,
1111 ];
1112
1113 wp_enqueue_script( SRFM_SLUG . '-suremail', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1114 wp_enqueue_style( SRFM_SLUG . '-suremail', SRFM_URL . 'assets/build/suremail.css', [], SRFM_VER, 'all' );
1115
1116 // Localize script for SureMail page.
1117 $suremail_localization_data = [
1118 'ajax_url' => admin_url( 'admin-ajax.php' ),
1119 'admin_url' => admin_url(),
1120 'suremail_url' => 'https://sureforms.com/suremail/',
1121 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
1122 'sfPluginManagerNonce' => wp_create_nonce( 'sf_plugin_manager_nonce' ),
1123 'suremail_status' => file_exists( WP_PLUGIN_DIR . '/suremails/suremails.php' )
1124 ? ( is_plugin_active( 'suremails/suremails.php' ) ? 'active' : 'installed' )
1125 : 'not_installed',
1126 ];
1127
1128 wp_localize_script(
1129 SRFM_SLUG . '-suremail',
1130 SRFM_SLUG . '_admin',
1131 apply_filters(
1132 SRFM_SLUG . '_suremail_admin_filter',
1133 $suremail_localization_data
1134 )
1135 );
1136
1137 $script_translations_handlers[] = SRFM_SLUG . '-suremail';
1138 }
1139
1140 // Enqueue scripts for the Quiz empty state page (free users only).
1141 if ( $is_screen_quiz_empty_state && ! Helper::has_pro() ) {
1142 $asset_handle = 'quizEmptyState';
1143
1144 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1145 $script_info = file_exists( $script_asset_path )
1146 ? include $script_asset_path
1147 : [
1148 'dependencies' => [],
1149 'version' => SRFM_VER,
1150 ];
1151
1152 wp_enqueue_script( SRFM_SLUG . '-quiz-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1153 wp_enqueue_style( SRFM_SLUG . '-quiz-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1154
1155 $script_translations_handlers[] = SRFM_SLUG . '-quiz-empty-state';
1156 }
1157
1158 // Enqueue scripts for the Survey Reports empty state page (free users only).
1159 if ( $is_screen_survey_empty_state && ! Helper::has_pro() ) {
1160 $asset_handle = 'surveyEmptyState';
1161
1162 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1163 $script_info = file_exists( $script_asset_path )
1164 ? include $script_asset_path
1165 : [
1166 'dependencies' => [],
1167 'version' => SRFM_VER,
1168 ];
1169
1170 wp_enqueue_script( SRFM_SLUG . '-survey-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1171 wp_enqueue_style( SRFM_SLUG . '-survey-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1172
1173 $script_translations_handlers[] = SRFM_SLUG . '-survey-empty-state';
1174 }
1175
1176 // Admin Submenu Styles.
1177 wp_enqueue_style( SRFM_SLUG . '-admin', $css_uri . 'backend/admin' . $file_prefix . $rtl . '.css', [], SRFM_VER );
1178
1179 if ( $is_screen_sureforms_form_settings ) {
1180 $asset_handle = 'settings';
1181
1182 $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1183 $script_info = file_exists( $script_asset_path )
1184 ? include $script_asset_path
1185 : [
1186 'dependencies' => [],
1187 'version' => SRFM_VER,
1188 ];
1189
1190 wp_enqueue_script( SRFM_SLUG . '-settings', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1191 wp_localize_script(
1192 SRFM_SLUG . '-settings',
1193 SRFM_SLUG . '_admin',
1194 apply_filters(
1195 SRFM_SLUG . '_admin_filter',
1196 $localization_data
1197 )
1198 );
1199
1200 $script_translations_handlers[] = SRFM_SLUG . '-settings';
1201 }
1202
1203 if ( $is_screen_add_new_form ) {
1204 wp_enqueue_style( SRFM_SLUG . '-template-picker', $css_uri . 'template-picker' . $file_prefix . $rtl . '.css', [], SRFM_VER );
1205
1206 $sureforms_admin = 'templatePicker';
1207
1208 $script_asset_path = SRFM_DIR . 'assets/build/' . $sureforms_admin . '.asset.php';
1209 $script_info = file_exists( $script_asset_path )
1210 ? include $script_asset_path
1211 : [
1212 'dependencies' => [],
1213 'version' => SRFM_VER,
1214 ];
1215 wp_enqueue_script( SRFM_SLUG . '-template-picker', SRFM_URL . 'assets/build/' . $sureforms_admin . '.js', $script_info['dependencies'], SRFM_VER, true );
1216
1217 wp_localize_script(
1218 SRFM_SLUG . '-template-picker',
1219 SRFM_SLUG . '_admin',
1220 [
1221 'site_url' => get_site_url(),
1222 'plugin_url' => SRFM_URL,
1223 'admin_url' => admin_url( 'admin.php' ),
1224 'new_template_picker_base_url' => admin_url( 'post-new.php?post_type=sureforms_form' ),
1225 'capability' => Helper::current_user_can(),
1226 'template_picker_nonce' => Helper::current_user_can() ? wp_create_nonce( 'wp_rest' ) : '',
1227 'is_pro_active' => Helper::has_pro(),
1228 'srfm_ai_usage_details' => AI_Helper::get_current_usage_details(),
1229 'is_pro_license_active' => AI_Helper::is_pro_license_active(),
1230 'srfm_ai_auth_user_email' => get_option( 'srfm_ai_auth_user_email' ),
1231 'pricing_page_url' => Helper::get_sureforms_website_url( 'pricing' ),
1232 'licensing_nonce' => wp_create_nonce( 'srfm_pro_licensing_nonce' ),
1233 ]
1234 );
1235
1236 $script_translations_handlers[] = SRFM_SLUG . '-template-picker';
1237 }
1238 // Quick action sidebar.
1239 $default_allowed_quick_sidebar_blocks = apply_filters(
1240 'srfm_quick_sidebar_allowed_blocks',
1241 [
1242 'srfm/input',
1243 'srfm/email',
1244 'srfm/textarea',
1245 'srfm/checkbox',
1246 'srfm/number',
1247 'srfm/inline-button',
1248 'srfm/advanced-heading',
1249 'srfm/payment',
1250 ]
1251 );
1252 if ( ! is_array( $default_allowed_quick_sidebar_blocks ) ) {
1253 $default_allowed_quick_sidebar_blocks = [];
1254 }
1255
1256 $srfm_enable_quick_action_sidebar = get_option( 'srfm_enable_quick_action_sidebar' );
1257 if ( ! $srfm_enable_quick_action_sidebar ) {
1258 $srfm_enable_quick_action_sidebar = 'disabled';
1259 }
1260 $quick_sidebar_allowed_blocks = get_option( 'srfm_quick_sidebar_allowed_blocks' );
1261 $quick_sidebar_allowed_blocks = ! empty( $quick_sidebar_allowed_blocks ) && is_array( $quick_sidebar_allowed_blocks ) ? $quick_sidebar_allowed_blocks : $default_allowed_quick_sidebar_blocks;
1262 $srfm_ajax_nonce = wp_create_nonce( 'srfm_ajax_nonce' );
1263
1264 if ( Helper::is_sureforms_admin_page() ) {
1265 wp_enqueue_script( SRFM_SLUG . '-quick-action-siderbar', SRFM_URL . 'assets/build/quickActionSidebar.js', [], SRFM_VER, true );
1266 wp_localize_script(
1267 SRFM_SLUG . '-quick-action-siderbar',
1268 SRFM_SLUG . '_quick_sidebar_blocks',
1269 [
1270 'allowed_blocks' => $quick_sidebar_allowed_blocks,
1271 'srfm_enable_quick_action_sidebar' => $srfm_enable_quick_action_sidebar,
1272 'srfm_ajax_nonce' => $srfm_ajax_nonce,
1273 'srfm_ajax_url' => admin_url( 'admin-ajax.php' ),
1274 ]
1275 );
1276
1277 $script_translations_handlers[] = SRFM_SLUG . '-quick-action-siderbar';
1278 }
1279
1280 /**
1281 * Enqueuing SureTriggers Integration script.
1282 * This script loads suretriggers iframe in Intergations tab.
1283 */
1284 if ( $is_post_type_sureforms_form ) {
1285 wp_enqueue_script( SRFM_SLUG . '-suretriggers-integration', SRFM_SURETRIGGERS_INTEGRATION_BASE_URL . 'js/v2/embed.js', [], SRFM_VER, true );
1286 }
1287
1288 // Check $script_translations_handlers is not empty before calling the function.
1289 if ( ! empty( $script_translations_handlers ) ) {
1290 // Remove duplicates values from the array.
1291 $script_translations_handlers = array_unique( $script_translations_handlers );
1292
1293 foreach ( $script_translations_handlers as $script_handle ) {
1294 Helper::register_script_translations( $script_handle );
1295 }
1296 }
1297 }
1298
1299 /**
1300 * Form Template Picker Admin Body Classes
1301 * WordPress sometimes translates class names in the admin body tag, which can result in
1302 * incorrect or missing class names when rendering the admin pages. This function ensures
1303 * that essential class names are manually added to the body tag to maintain proper functionality.
1304 *
1305 * @since 0.0.1
1306 * @param string $classes Space separated class string.
1307 */
1308 public function admin_template_picker_body_class( $classes = '' ) {
1309 // Define an associative array of class names and their corresponding conditions.
1310 // Each condition checks whether a specific request context matches.
1311 $srfm_classes = [
1312 'sureforms_page_sureforms_entries' => Helper::validate_request_context( SRFM_ENTRIES, 'page' ),
1313 'sureforms_page_sureforms_form_settings' => Helper::validate_request_context( 'sureforms_form_settings', 'page' ),
1314 'srfm-template-picker' => Helper::validate_request_context( 'add-new-form', 'page' ),
1315 ];
1316
1317 $add_srfm_classes = '';
1318
1319 // Loop through the defined classes and conditions.
1320 foreach ( $srfm_classes as $class => $condition ) {
1321 // Check if the condition evaluates to true.
1322 if ( $condition ) {
1323 // Append the class to the existing classes string, followed by a space.
1324 $add_srfm_classes .= empty( $add_srfm_classes ) ? $class : ' ' . $class;
1325 }
1326 }
1327
1328 // Append the new classes to the existing classes string.
1329 if ( ! empty( $add_srfm_classes ) ) {
1330 $classes .= ' ' . $add_srfm_classes;
1331 }
1332
1333 // Return the updated list of classes.
1334 return $classes;
1335 }
1336
1337 /**
1338 * Disable spectra's quick action bar in sureforms CPT.
1339 *
1340 * @param string $status current status of the quick action bar.
1341 * @since 0.0.2
1342 * @return string
1343 */
1344 public function restrict_spectra_quick_action_bar( $status ) {
1345 $screen = get_current_screen();
1346 if ( 'disabled' !== $status && isset( $screen->id ) && 'sureforms_form' === $screen->id ) {
1347 $status = 'disabled';
1348 }
1349
1350 return $status;
1351 }
1352
1353 /**
1354 * Register Pro compatibility notices early for React pages.
1355 *
1356 * This method runs on admin_init (priority 5) to ensure notices are
1357 * registered BEFORE admin_enqueue_scripts, so they're available when
1358 * wp_localize_script runs.
1359 *
1360 * Hooked - admin_init (priority 5)
1361 *
1362 * @return void
1363 * @since 2.5.0
1364 */
1365 public function register_pro_compatibility_notices() {
1366 // Early exit if Pro is not active, user lacks permissions, or Notice_Manager is unavailable.
1367 if ( ! Helper::has_pro() || ! Helper::current_user_can() || ! class_exists( 'SRFM\Admin\Notice_Manager' ) ) {
1368 return;
1369 }
1370
1371 // Register version outdated notice for React pages.
1372 if ( ! version_compare( SRFM_PRO_VER, SRFM_PRO_RECOMMENDED_VER, '>=' ) ) {
1373 $pro_plugin_name = defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'SureForms Pro';
1374 $react_outdated_message = sprintf(
1375 // translators: %1$s: SureForms version, %2$s: SureForms Pro Plugin Name, %3$s: SureForms Pro Version.
1376 esc_html__( 'SureForms %1$s requires minimum %2$s %3$s to work properly. Please update to the latest version.', 'sureforms' ),
1377 esc_html( SRFM_VER ),
1378 esc_html( $pro_plugin_name ),
1379 esc_html( SRFM_PRO_RECOMMENDED_VER )
1380 );
1381
1382 \SRFM\Admin\Notice_Manager::register_notice(
1383 [
1384 'id' => 'sureforms-pro-version-outdated',
1385 'variant' => 'warning',
1386 'message' => $react_outdated_message,
1387 'actions' => [
1388 [
1389 'label' => esc_html__( 'Update Now', 'sureforms' ),
1390 'url' => admin_url( 'update-core.php' ),
1391 'variant' => 'primary',
1392 ],
1393 ],
1394 'pages' => [ 'all' ],
1395 ]
1396 );
1397 }
1398 }
1399
1400 /**
1401 * Admin Notice Callback if sureforms pro is out of date.
1402 *
1403 * Hooked - admin_notices
1404 *
1405 * @return void
1406 * @since 1.0.4
1407 */
1408 public function srfm_pro_version_compatibility() {
1409 if ( ! Helper::has_pro() ) {
1410 return;
1411 }
1412
1413 if ( empty( get_current_screen() ) ) {
1414 return;
1415 }
1416
1417 if ( ! Helper::current_user_can() ) {
1418 return;
1419 }
1420
1421 $srfm_pro_license_status = get_option( 'srfm_pro_license_status', '' );
1422 /**
1423 * If the license status is not set then get the license status and update the option accordingly.
1424 * This will be executed only once. Subsequently, the option status is updated by the licensing class on license activation or deactivation.
1425 */
1426 if ( empty( $srfm_pro_license_status ) && class_exists( 'SRFM_PRO\Admin\Licensing' ) ) {
1427 $srfm_pro_license_status = \SRFM_PRO\Admin\Licensing::is_license_active() ? 'licensed' : 'unlicensed';
1428 update_option( 'srfm_pro_license_status', $srfm_pro_license_status );
1429 }
1430
1431 $pro_plugin_name = defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'SureForms Pro';
1432 $message = '';
1433 $url = admin_url( 'admin.php?page=sureforms_form_settings&tab=account-settings' );
1434 if ( 'unlicensed' === $srfm_pro_license_status ) {
1435 ob_start();
1436 ?>
1437 <p>
1438 <?php
1439 printf(
1440 // translators: %1$s: Opening anchor tag with URL, %2$s: Closing anchor tag, %3$s: SureForms Pro Plugin Name.
1441 esc_html__( 'Please %1$sactivate%2$s your copy of %3$s to get new features, access support, receive update notifications, and more.', 'sureforms' ),
1442 '<a href="' . esc_url( $url ) . '">',
1443 '</a>',
1444 '<i>' . esc_html( $pro_plugin_name ) . '</i>'
1445 );
1446 ?>
1447 </p>
1448 <?php
1449 $message = ob_get_clean();
1450 }
1451
1452 if ( ! version_compare( SRFM_PRO_VER, SRFM_PRO_RECOMMENDED_VER, '>=' ) ) {
1453 ob_start();
1454 ?>
1455 <p>
1456 <?php
1457 printf(
1458 // 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.
1459 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' ),
1460 esc_html( SRFM_VER ),
1461 esc_html( $pro_plugin_name ),
1462 esc_html( SRFM_PRO_RECOMMENDED_VER ),
1463 '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">',
1464 '</a>'
1465 );
1466 ?>
1467 </p>
1468 <?php
1469 $message .= ob_get_clean();
1470 }
1471
1472 if ( ! empty( $message ) ) {
1473 // Phpcs ignore comment is required as $message variable is already escaped.
1474 ?>
1475 <div class="notice notice-warning"><?php echo wp_kses_post( $message ); ?></div>
1476 <?php
1477 }
1478 }
1479
1480 /**
1481 * Display a notice to the user about providing a review.
1482 *
1483 * @since 2.5.2
1484 * @return void
1485 */
1486 public function display_srfm_rating_notice() {
1487 // Only show to admins.
1488 if ( ! Helper::current_user_can() ) {
1489 return;
1490 }
1491
1492 // Allow the notice to be disabled.
1493 if ( ! apply_filters( 'srfm_show_rating_notice', true ) ) {
1494 return;
1495 }
1496
1497 Astra_Notices::add_notice(
1498 [
1499 'id' => 'srfm-plugin-review-notice',
1500 'type' => '',
1501 'message' => $this->build_notice_markup(
1502 esc_html__( 'Amazing! SureForms is powering your forms and submissions - let\'s keep growing together!', 'sureforms' ),
1503 esc_html__( 'If SureForms has been helpful, would you mind taking a moment to leave a 5-star review on WordPress.org?', 'sureforms' ),
1504 esc_url( 'https://wordpress.org/support/plugin/sureforms/reviews/?filter=5#new-post' ),
1505 esc_html__( 'Rate SureForms', 'sureforms' ),
1506 esc_html__( 'Maybe later', 'sureforms' ),
1507 esc_html__( 'I already did', 'sureforms' ),
1508 WEEK_IN_SECONDS,
1509 true
1510 ),
1511 'repeat-notice-after' => WEEK_IN_SECONDS,
1512 'show_if' => $this->maybe_display_rating_notice(),
1513 'display-with-other-notices' => true,
1514 ]
1515 );
1516
1517 add_action( 'astra_notice_after_markup_srfm-plugin-review-notice', [ $this, 'enqueue_notice_response_script' ] );
1518 }
1519
1520 /**
1521 * Display a "Getting Started" admin notice for new users who haven't yet
1522 * reached the rating-notice milestone (3+ forms or 3+ entries).
1523 *
1524 * The Astra Notices library handles the 7-day delay via the
1525 * `display-notice-after` parameter.
1526 *
1527 * @since 2.5.2
1528 * @return void
1529 */
1530 public function display_srfm_getting_started_notice() {
1531 // Only show to admins.
1532 if ( ! Helper::current_user_can() ) {
1533 return;
1534 }
1535
1536 // Allow the notice to be disabled programmatically.
1537 if ( ! apply_filters( 'srfm_show_getting_started_notice', true ) ) {
1538 return;
1539 }
1540
1541 Astra_Notices::add_notice(
1542 [
1543 'id' => 'srfm-getting-started-notice',
1544 'type' => '',
1545 'message' => $this->build_notice_markup(
1546 esc_html__( 'SureForms is ready to power your forms — explore what\'s possible!', 'sureforms' ),
1547 esc_html__( 'Manage your forms, track submissions, and discover features like AI Form Builder, payment integrations, and more from the SureForms dashboard.', 'sureforms' ),
1548 esc_url( admin_url( 'admin.php?page=sureforms_menu' ) ),
1549 esc_html__( 'Go to Dashboard', 'sureforms' ),
1550 esc_html__( 'Maybe later', 'sureforms' ),
1551 esc_html__( 'I already know', 'sureforms' ),
1552 WEEK_IN_SECONDS
1553 ),
1554 'repeat-notice-after' => WEEK_IN_SECONDS,
1555 'show_if' => ! $this->maybe_display_rating_notice(),
1556 'display-notice-after' => WEEK_IN_SECONDS,
1557 'display-with-other-notices' => true,
1558 ]
1559 );
1560
1561 add_action( 'astra_notice_after_markup_srfm-getting-started-notice', [ $this, 'enqueue_notice_response_script' ] );
1562 }
1563
1564 /**
1565 * Enqueue the notice response analytics script.
1566 *
1567 * Called via the astra_notice_after_markup_{id} hook so the script
1568 * only loads when a SureForms notice is actually rendered.
1569 *
1570 * @since 2.5.2
1571 * @return void
1572 */
1573 public function enqueue_notice_response_script() {
1574 if ( wp_script_is( 'srfm-notice-response', 'enqueued' ) ) {
1575 return;
1576 }
1577
1578 wp_enqueue_script(
1579 'srfm-notice-response',
1580 SRFM_URL . 'admin/assets/js/notice-response.js',
1581 [],
1582 SRFM_VER,
1583 true
1584 );
1585
1586 wp_localize_script(
1587 'srfm-notice-response',
1588 'srfmNoticeResponse',
1589 [
1590 'ajaxurl' => admin_url( 'admin-ajax.php' ),
1591 'nonce' => wp_create_nonce( 'srfm_notice_response' ),
1592 ]
1593 );
1594 }
1595
1596 /**
1597 * Handle the notice response AJAX request.
1598 *
1599 * Validates the request and records the analytics event
1600 * for the notice button that was clicked.
1601 *
1602 * @since 2.5.2
1603 * @return void
1604 */
1605 public function handle_notice_response() {
1606 if ( ! check_ajax_referer( 'srfm_notice_response', 'nonce', false ) ) {
1607 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1608 }
1609
1610 if ( ! Helper::current_user_can() ) {
1611 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1612 }
1613
1614 $notice_id = isset( $_POST['notice_id'] ) ? sanitize_text_field( wp_unslash( $_POST['notice_id'] ) ) : '';
1615 $button = isset( $_POST['button'] ) ? sanitize_text_field( wp_unslash( $_POST['button'] ) ) : '';
1616
1617 $valid = [
1618 'srfm-getting-started-notice' => [
1619 'go_to_dashboard' => 'getting_started_notice_cta',
1620 'maybe_later' => 'getting_started_notice_snooze',
1621 'dismissed' => 'getting_started_notice_dismiss',
1622 ],
1623 'srfm-plugin-review-notice' => [
1624 'rate_sureforms' => 'rating_notice_cta',
1625 'maybe_later' => 'rating_notice_snooze',
1626 'dismissed' => 'rating_notice_dismiss',
1627 ],
1628 ];
1629
1630 if ( ! isset( $valid[ $notice_id ][ $button ] ) ) {
1631 wp_send_json_error( [ 'message' => __( 'Invalid parameters.', 'sureforms' ) ], 400 );
1632 }
1633
1634 $event_name = $valid[ $notice_id ][ $button ];
1635 Analytics::events()->track( $event_name, $button );
1636
1637 wp_send_json_success();
1638 }
1639
1640 /**
1641 * Disables the capabilities for WPForms to avoid conflicts when enqueueing
1642 * scripts and styles for WPForms.
1643 *
1644 * This function is intended to prevent any potential conflicts that may arise
1645 * when WPForms scripts and styles are enqueued. By disabling certain capabilities,
1646 * it ensures that WPForms does not interfere with other functionalities.
1647 *
1648 * @param bool $user_can A boolean indicating whether the user has the capability.
1649 * @return bool Returns true if the capabilities are successfully disabled, false otherwise.
1650 * @since 1.4.2
1651 */
1652 public function disable_wpforms_capabilities( $user_can ) {
1653 // Note: Nonce verification is intentionally omitted here as no database operations are performed.
1654 // The values of the $_REQUEST variables are strictly validated, ensuring security without the need for nonce verification.
1655
1656 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1657 $post_id = ! empty( $_REQUEST['post'] ) && ! empty( $_REQUEST['action'] ) ? absint( $_REQUEST['post'] ) : 0;
1658
1659 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1660 $post_type = $post_id ? get_post_type( $post_id ) : sanitize_text_field( wp_unslash( $_REQUEST['post_type'] ?? '' ) );
1661 return SRFM_FORMS_POST_TYPE === $post_type ? false : $user_can;
1662 }
1663
1664 /**
1665 * Enqueueus the admin pointer script and styles.
1666 *
1667 * @return void
1668 * @since 1.8.0
1669 */
1670 public function enqueue_admin_pointer() {
1671 if ( ! $this->is_admin_pointer_visible() ) {
1672 return;
1673 }
1674 wp_enqueue_style( 'wp-pointer' );
1675 wp_enqueue_script( 'wp-pointer' );
1676 wp_enqueue_script(
1677 'sureforms-admin-pointer',
1678 plugins_url( 'admin/assets/js/sureforms-pointer.js', SRFM_FILE ),
1679 [ 'wp-pointer', 'jquery' ],
1680 SRFM_VER,
1681 true
1682 );
1683 wp_localize_script(
1684 'sureforms-admin-pointer',
1685 'sureformsPointerData',
1686 [
1687 'ajaxurl' => admin_url( 'admin-ajax.php' ),
1688 'pointer_nonce' => wp_create_nonce( 'sureforms_pointer_action' ),
1689 ]
1690 );
1691 }
1692
1693 /**
1694 * Ajax handler for pointer popup visibility.
1695 *
1696 * @return void
1697 * @since 1.8.0
1698 */
1699 public function pointer_should_show() {
1700 // Security: Check user capability.
1701 if ( ! Helper::current_user_can() ) {
1702 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1703 }
1704 // Security: Nonce check.
1705 if ( empty( $_POST['pointer_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pointer_nonce'] ) ), 'sureforms_pointer_action' ) ) {
1706 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1707 }
1708
1709 $content_markup = sprintf(
1710 /* translators: 1: opening span, 2: opening strong (inline), 3: closing strong, 4: closing span, 5: opening strong (block), 6: closing strong */
1711 __( '%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' ),
1712 '<span>',
1713 '<strong>',
1714 '</strong>',
1715 '</span><br/>',
1716 '<strong style="font-size:1.1em;">',
1717 '</strong>'
1718 );
1719 wp_send_json(
1720 [
1721 'show' => true,
1722 'title' => esc_html( __( 'SureForms is waiting for you!', 'sureforms' ) ),
1723 'content' => wp_kses_post( $content_markup ),
1724 'button_text' => esc_html( __( 'Build My First Form', 'sureforms' ) ),
1725 'dismiss' => esc_html( __( 'Dismiss', 'sureforms' ) ),
1726 'button_url' => admin_url( 'admin.php?page=add-new-form' ),
1727 ]
1728 );
1729 }
1730
1731 /**
1732 * Ajax callback for pointer popup dismissed action.
1733 *
1734 * @return void
1735 * @since 1.8.0
1736 */
1737 public function pointer_dismissed() {
1738 // Security: Check user capability.
1739 if ( ! Helper::current_user_can() ) {
1740 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1741 }
1742 // Security: Nonce check.
1743 if ( empty( $_POST['pointer_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pointer_nonce'] ) ), 'sureforms_pointer_action' ) ) {
1744 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1745 }
1746 // Use Helper to update srfm_options key.
1747 Helper::update_srfm_option( 'pointer_popup_dismissed', time() );
1748
1749 wp_send_json_success();
1750 }
1751
1752 /**
1753 * Ajax pointer accepted CTA callback.
1754 *
1755 * @return void
1756 * @since 1.8.0
1757 */
1758 public function pointer_accepted_cta() {
1759 // Security: Check user capability.
1760 if ( ! Helper::current_user_can() ) {
1761 wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
1762 }
1763 // Security: Nonce check.
1764 if ( empty( $_POST['pointer_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pointer_nonce'] ) ), 'sureforms_pointer_action' ) ) {
1765 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
1766 }
1767 // Use Helper to update srfm_options key.
1768 Helper::update_srfm_option( 'pointer_popup_accepted', time() );
1769
1770 wp_send_json_success();
1771 }
1772
1773 /**
1774 * Maybe register the dashboard widget based on entries.
1775 *
1776 * @return void
1777 * @since 1.9.1
1778 */
1779 public function maybe_register_dashboard_widget() {
1780
1781 // Only for users with manage_options capability.
1782 if ( ! Helper::current_user_can() ) {
1783 return;
1784 }
1785
1786 // Quick check if there are any entries in the last 7 days.
1787 $seven_days_ago = strtotime( '-7 days' );
1788 $total_entries = Entries::get_entries_count_after( $seven_days_ago );
1789
1790 // Only add the dashboard setup hook if there are entries.
1791 if ( $total_entries > 0 ) {
1792 // Get forms with entries (limit 4 for dashboard widget).
1793 $this->dashboard_widget_data = Helper::get_forms_with_entry_counts( $seven_days_ago, 4 );
1794
1795 // Only show dashboard widget if there are forms with entries.
1796 if ( ! empty( $this->dashboard_widget_data ) ) {
1797 add_action( 'wp_dashboard_setup', [ $this, 'register_dashboard_widget' ] );
1798 }
1799 }
1800 }
1801
1802 /**
1803 * Register the dashboard widget.
1804 *
1805 * @return void
1806 * @since 1.9.1
1807 */
1808 public function register_dashboard_widget() {
1809 // Add the widget with high priority to position it at the top.
1810 wp_add_dashboard_widget(
1811 'sureforms_recent_entries',
1812 __( 'SureForms', 'sureforms' ),
1813 [ $this, 'render_dashboard_widget' ],
1814 null,
1815 null,
1816 'normal',
1817 'high'
1818 );
1819 }
1820
1821 /**
1822 * Render the dashboard widget content.
1823 *
1824 * @return void
1825 * @since 1.9.1
1826 */
1827 public function render_dashboard_widget() {
1828 // Use the pre-fetched data to avoid duplicate queries.
1829 $entries_data = $this->dashboard_widget_data;
1830
1831 // Display the widget content.
1832 ?>
1833 <div class="srfm-dashboard-widget">
1834 <div class="srfm-widget-header">
1835 <h3 class="srfm-widget-title">
1836 <?php esc_html_e( 'Recent Entries', 'sureforms' ); ?>
1837 <span class="srfm-widget-subtitle"><?php esc_html_e( '( Last 7 days )', 'sureforms' ); ?></span>
1838 </h3>
1839 <a href="<?php echo esc_url( admin_url( 'admin.php?page=sureforms_entries' ) ); ?>" class="srfm-widget-view-link">
1840 <?php esc_html_e( 'View', 'sureforms' ); ?>
1841 </a>
1842 </div>
1843
1844 <div class="srfm-table-wrapper">
1845 <table class="srfm-entries-table">
1846 <thead>
1847 <tr>
1848 <th><?php esc_html_e( 'Form Name', 'sureforms' ); ?></th>
1849 <th><?php esc_html_e( 'Entries', 'sureforms' ); ?></th>
1850 </tr>
1851 </thead>
1852 <tbody>
1853 <?php foreach ( $entries_data as $form_data ) { ?>
1854 <tr>
1855 <td class="form-name"><?php echo esc_html( $form_data['title'] ); ?></td>
1856 <td class="entry-count"><?php echo esc_html( $form_data['count'] ); ?></td>
1857 </tr>
1858 <?php } ?>
1859 </tbody>
1860 </table>
1861 </div>
1862
1863 <?php
1864 // Render footer if applicable.
1865 $this->render_dashboard_widget_footer( $entries_data );
1866 ?>
1867 </div>
1868 <?php
1869 }
1870
1871 /**
1872 * Build the shared HTML markup for admin notices.
1873 *
1874 * @since 2.5.2
1875 *
1876 * All text parameters must be pre-escaped by the caller (e.g. via esc_html__()).
1877 * URL parameters must be pre-escaped via esc_url().
1878 *
1879 * @param string $heading The notice heading text (pre-escaped).
1880 * @param string $message The notice body text (pre-escaped).
1881 * @param string $cta_url The primary CTA URL (pre-escaped).
1882 * @param string $cta_text The primary CTA button text (pre-escaped).
1883 * @param string $snooze_text The snooze button text (pre-escaped).
1884 * @param string $dismiss_text The dismiss button text (pre-escaped).
1885 * @param int $snooze_duration Snooze duration in seconds for the data-repeat-notice-after attribute.
1886 * @param bool $external_cta Whether the CTA opens in a new tab and also dismisses the notice
1887 * via the astra-notice-close class. Default false.
1888 * @return string The notice HTML markup.
1889 */
1890 private function build_notice_markup( $heading, $message, $cta_url, $cta_text, $snooze_text, $dismiss_text, $snooze_duration, $external_cta = false ) {
1891 $image_path = esc_url( SRFM_URL . 'admin/assets/sureforms-logo.png' );
1892 $cta_class = $external_cta ? 'astra-notice-close button-primary' : 'button-primary';
1893 $cta_attrs = $external_cta ? ' target="_blank" rel="noopener noreferrer"' : '';
1894
1895 return sprintf(
1896 '<div class="notice-image">
1897 <img src="%1$s" class="custom-logo" alt="SureForms" itemprop="logo">
1898 </div>
1899 <div class="notice-content">
1900 <div class="notice-heading">
1901 %2$s
1902 </div>
1903 %3$s<br />
1904 <div class="astra-review-notice-container">
1905 <a href="%4$s" class="%5$s"%6$s>
1906 %7$s
1907 </a>
1908 <span class="dashicons dashicons-clock" aria-hidden="true"></span>
1909 <a href="#" data-repeat-notice-after="%8$s" class="astra-notice-close">
1910 %9$s
1911 </a>
1912 <span class="dashicons dashicons-smiley" aria-hidden="true"></span>
1913 <a href="#" class="astra-notice-close">
1914 %10$s
1915 </a>
1916 </div>
1917 </div>',
1918 $image_path,
1919 $heading,
1920 $message,
1921 $cta_url,
1922 esc_attr( $cta_class ),
1923 $cta_attrs,
1924 $cta_text,
1925 $snooze_duration,
1926 $snooze_text,
1927 $dismiss_text
1928 );
1929 }
1930
1931 /**
1932 * Callback for displaying the rating notice conditionally.
1933 *
1934 * Returns true if the user has 3 or more published forms or 3 or more form entries.
1935 *
1936 * @since 2.5.2
1937 * @return bool
1938 */
1939 private function maybe_display_rating_notice() {
1940 if ( null === $this->should_show_rating ) {
1941 $entries_count = Entries::get_total_entries_by_status( 'all' );
1942 $form_count = wp_count_posts( SRFM_FORMS_POST_TYPE );
1943 $this->should_show_rating = $entries_count >= self::RATING_NOTICE_THRESHOLD || Helper::get_integer_value( $form_count->publish ?? 0 ) >= self::RATING_NOTICE_THRESHOLD;
1944 }
1945
1946 return $this->should_show_rating;
1947 }
1948
1949 /**
1950 * Get random premium feature text.
1951 *
1952 * @return string Random feature text.
1953 * @since 1.9.1
1954 */
1955 private function get_random_premium_feature_text() {
1956 $features = [
1957 __( 'Use Conditional Logic to show only what matters', 'sureforms' ),
1958 __( 'Split your form into steps to keep it easy', 'sureforms' ),
1959 __( 'Let people upload files directly to your form', 'sureforms' ),
1960 __( 'Turn responses into downloadable PDFs automatically', 'sureforms' ),
1961 __( 'Let users sign with a simple signature field', 'sureforms' ),
1962 __( 'Connect your form to other tools using webhooks', 'sureforms' ),
1963 __( 'Use Conversational Forms for a chat-like experience', 'sureforms' ),
1964 __( 'Let users register or log in through your form', 'sureforms' ),
1965 __( 'Build forms that create WordPress user accounts', 'sureforms' ),
1966 __( 'Add calculations to auto-total scores or prices', 'sureforms' ),
1967 ];
1968
1969 // Get a random feature.
1970 $random_key = array_rand( $features );
1971 return $features[ $random_key ];
1972 }
1973
1974 /**
1975 * Render the dashboard widget footer for upsell.
1976 *
1977 * @param array $entries_data The entries data array.
1978 * @return void
1979 * @since 1.9.1
1980 */
1981 private function render_dashboard_widget_footer( $entries_data ) {
1982 // Only show footer if Pro is not active.
1983 if ( Helper::has_pro() ) {
1984 return;
1985 }
1986
1987 // Count total entries in last 7 days.
1988 $total_entries = 0;
1989 foreach ( $entries_data as $form_data ) {
1990 $total_entries += $form_data['count'];
1991 }
1992
1993 // Count total published forms.
1994 $published_forms_count = wp_count_posts( SRFM_FORMS_POST_TYPE )->publish;
1995
1996 // Show footer only if 3+ entries received OR 3+ forms published.
1997 if ( $total_entries >= 3 || $published_forms_count >= 3 ) {
1998 ?>
1999 <div class="srfm-widget-footer">
2000 <div class="srfm-upgrade-content">
2001 <svg class="srfm-logo-icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2002 <rect width="20" height="20" fill="#D54407"/>
2003 <path d="M5.7139 4.2854H14.2853V7.1425H7.1424L5.7139 8.5711V7.1425V4.2854Z" fill="white"/>
2004 <path d="M5.7139 4.2854H14.2853V7.1425H7.1424L5.7139 8.5711V7.1425V4.2854Z" fill="white"/>
2005 <path d="M5.7148 8.5713H12.8577V11.4284H7.1434L5.7148 12.857V11.4284V8.5713Z" fill="white"/>
2006 <path d="M5.7148 8.5713H12.8577V11.4284H7.1434L5.7148 12.857V11.4284V8.5713Z" fill="white"/>
2007 <path d="M5.7148 12.8569H10.0006V15.7141H5.7148V12.8569Z" fill="white"/>
2008 <path d="M5.7148 12.8569H10.0006V15.7141H5.7148V12.8569Z" fill="white"/>
2009 </svg>
2010 <span><?php echo esc_html( $this->get_random_premium_feature_text() ); ?></span>
2011 </div>
2012 <?php
2013 $upgrade_url = add_query_arg(
2014 [
2015 'utm_medium' => 'dashboard-widget',
2016 ],
2017 Helper::get_sureforms_website_url( 'pricing' )
2018 );
2019 ?>
2020 <a href="<?php echo esc_url( $upgrade_url ); ?>" class="srfm-upgrade-link" target="_blank">
2021 <?php esc_html_e( 'Upgrade', 'sureforms' ); ?>
2022 </a>
2023 </div>
2024 <?php
2025 }
2026 }
2027
2028 /**
2029 * Determine if the admin pointer should be visible on this page.
2030 *
2031 * @since 1.8.0
2032 * @return bool
2033 */
2034 private function is_admin_pointer_visible() {
2035 global $pagenow;
2036 $allowed_pages = [ 'index.php', 'options-general.php' ];
2037
2038 // Do not show if pointer dismissed, accepted, or more than 1 form exists.
2039 if (
2040 ! empty( Helper::get_srfm_option( 'pointer_popup_dismissed' ) )
2041 || ! empty( Helper::get_srfm_option( 'pointer_popup_accepted' ) )
2042 || (int) ( wp_count_posts( SRFM_FORMS_POST_TYPE )->publish ?? 0 ) > 1
2043 ) {
2044 return false;
2045 }
2046
2047 if ( in_array( $pagenow, $allowed_pages, true ) ) {
2048 return true;
2049 }
2050
2051 return false;
2052 }
2053
2054 }
2055