PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.5.2
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.5.2
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
← All changes | admin/admin.php +93 -590 2.12.12.5.2 View file →
@@ -8,15 +8,14 @@
8 8 namespace SRFM\Admin;
9 9
10 10 use Astra_Notices;
11 11 use SRFM\Inc\AI_Form_Builder\AI_Helper;
12 +use SRFM\Inc\Analytics_Events;
12 13 use SRFM\Inc\Database\Tables\Entries;
13 -use SRFM\Inc\Global_Settings\Global_Settings;
14 14 use SRFM\Inc\Helper;
15 15 use SRFM\Inc\Onboarding;
16 16 use SRFM\Inc\Payments\Payment_Helper;
17 17 use SRFM\Inc\Payments\Stripe\Stripe_Helper;
18 -use SRFM\Inc\Smart_Tags;
19 18 use SRFM\Inc\Traits\Get_Instance;
20 19
21 20 if ( ! defined( 'ABSPATH' ) ) {
22 21 exit; // Exit if accessed directly.
@@ -21,10 +20,10 @@
21 20 if ( ! defined( 'ABSPATH' ) ) {
22 21 exit; // Exit if accessed directly.
23 22 }
24 23
25 -if ( ! class_exists( 'BSF_Admin_Notices' ) ) {
26 - require_once SRFM_DIR . 'inc/lib/astra-notices/class-bsf-admin-notices.php';
24 +if ( ! class_exists( 'Astra_Notices' ) ) {
25 + require_once SRFM_DIR . 'inc/lib/astra-notices/class-astra-notices.php';
27 26 }
28 27 /**
29 28 * Admin handler class.
30 29 *
@@ -40,19 +39,8 @@
40 39 */
41 40 public const RATING_NOTICE_THRESHOLD = 3;
42 41
43 42 /**
44 - * Inline CSS for Quill 1.x (react-quill) list markers.
45 - *
46 - * Quill 1.x renders bullet/numbered list markers via CSS ::before pseudo-elements,
47 - * whereas the vendor quill.snow.css targets .ql-ui child elements (Quill 2.x approach).
48 - * This constant is shared by enqueue_styles() and enqueue_scripts() to prevent drift.
49 - *
50 - * @since 2.5.2
51 - */
52 - public const QUILL_1X_INLINE_CSS = '.ql-editor ul,.ql-editor ol{padding-left:1.5em}.ql-editor ul>li,.ql-editor ol>li{list-style-type:none}.ql-editor ol li:not(.ql-direction-rtl),.ql-editor ul li:not(.ql-direction-rtl){padding-left:1.5em}.ql-editor ol li.ql-direction-rtl,.ql-editor ul li.ql-direction-rtl{padding-right:1.5em}.ql-editor ul>li::before{content:"\2022"}.ql-editor li::before{display:inline-block;white-space:nowrap;width:1.2em}.ql-editor li:not(.ql-direction-rtl)::before{margin-left:-1.5em;margin-right:.3em;text-align:right}.ql-editor li.ql-direction-rtl::before{margin-left:.3em;margin-right:-1.5em}.ql-editor ol li{counter-reset:list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;counter-increment:list-0}.ql-editor ol li::before{content:counter(list-0,decimal) ". "}.ql-editor ol li.ql-indent-1{counter-increment:list-1;counter-reset:list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-1::before{content:counter(list-1,lower-alpha) ". "}.ql-editor ol li.ql-indent-2{counter-increment:list-2;counter-reset:list-3 list-4 list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-2::before{content:counter(list-2,lower-roman) ". "}.ql-editor ol li.ql-indent-3{counter-increment:list-3;counter-reset:list-4 list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-3::before{content:counter(list-3,decimal) ". "}.ql-editor ol li.ql-indent-4{counter-increment:list-4;counter-reset:list-5 list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-4::before{content:counter(list-4,lower-alpha) ". "}.ql-editor ol li.ql-indent-5{counter-increment:list-5;counter-reset:list-6 list-7 list-8 list-9}.ql-editor ol li.ql-indent-5::before{content:counter(list-5,lower-roman) ". "}.ql-editor ol li.ql-indent-6{counter-increment:list-6;counter-reset:list-7 list-8 list-9}.ql-editor ol li.ql-indent-6::before{content:counter(list-6,decimal) ". "}.ql-editor ol li.ql-indent-7{counter-increment:list-7;counter-reset:list-8 list-9}.ql-editor ol li.ql-indent-7::before{content:counter(list-7,lower-alpha) ". "}.ql-editor ol li.ql-indent-8{counter-increment:list-8;counter-reset:list-9}.ql-editor ol li.ql-indent-8::before{content:counter(list-8,lower-roman) ". "}.ql-editor ol li.ql-indent-9{counter-increment:list-9}.ql-editor ol li.ql-indent-9::before{content:counter(list-9,decimal) ". "}';
53 -
54 - /**
55 43 * Dashboard widget entries data.
56 44 *
57 45 * @var array
58 46 * @since 1.9.1
@@ -88,11 +76,8 @@
88 76 add_action( 'admin_menu', [ $this, 'add_learn_page' ] );
89 77 add_action( 'admin_menu', [ $this, 'add_new_form' ] );
90 78 add_action( 'admin_menu', [ $this, 'add_suremail_page' ] );
91 79 if ( ! Helper::has_pro() ) {
92 - add_action( 'admin_menu', [ $this, 'add_quiz_page' ] );
93 - add_action( 'admin_menu', [ $this, 'add_survey_reports_page' ] );
94 - add_action( 'admin_menu', [ $this, 'add_partial_entries_page' ] );
95 80 add_action( 'admin_menu', [ $this, 'add_upgrade_to_pro' ] );
96 81 add_action( 'admin_footer', [ $this, 'add_upgrade_to_pro_target_attr' ] );
97 82 }
98 83
@@ -122,8 +107,10 @@
122 107 if ( $admin_notification_on ) {
123 108 add_action( 'admin_menu', [ $this, 'maybe_add_entries_badge' ], 99 );
124 109 }
125 110
111 + add_action( 'admin_menu', [ $this, 'add_payments_badge' ], 99 );
112 +
126 113 add_filter( 'wpforms_current_user_can', [ $this, 'disable_wpforms_capabilities' ], 10, 3 );
127 114
128 115 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_pointer' ] );
129 116 // Ajax callbacks for wp-pointer functionality.
@@ -130,96 +117,19 @@
130 117 add_action( 'wp_ajax_should_show_pointer', [ $this, 'pointer_should_show' ] );
131 118 add_action( 'wp_ajax_sureforms_dismiss_pointer', [ $this, 'pointer_dismissed' ] );
132 119 add_action( 'wp_ajax_sureforms_accept_cta', [ $this, 'pointer_accepted_cta' ] );
133 120 add_action( 'wp_ajax_srfm_notice_response', [ $this, 'handle_notice_response' ] );
134 - add_action( 'wp_ajax_srfm_ai_widget_usage', [ $this, 'track_ai_widget_usage' ] );
135 121
136 122 // Register dashboard widget only if there are recent entries.
137 123 add_action( 'admin_init', [ $this, 'maybe_register_dashboard_widget' ] );
138 124
139 - // Enqueue the AI quick draft widget script on the dashboard screen.
140 - add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_ai_dashboard_widget_assets' ] );
141 -
142 125 // Save first form creation time stamp.
143 126 add_action( 'admin_init', [ $this, 'save_first_form_creation_time_stamp' ] );
144 127 add_action( 'admin_notices', [ $this, 'display_srfm_rating_notice' ] );
145 128 add_action( 'admin_notices', [ $this, 'display_srfm_getting_started_notice' ] );
146 -
147 - /**
148 - * Suppress foreign (third-party) admin notices on SureForms admin screens.
149 - *
150 - * Some plugins (e.g. Ninja Forms) print large promotional banners on every
151 - * admin page via the admin_notices / all_admin_notices / network_admin_notices
152 - * hooks. These bleed onto SureForms' own React admin screens and break the UI.
153 - * We run at the EARLIEST priority on each notice hook (all third-party
154 - * callbacks are registered before these hooks fire, during admin_init /
155 - * plugin load) and strip the foreign ones before they are echoed, while
156 - * preserving SureForms' own notices. Scoped strictly to SureForms screens.
157 - */
158 - add_action( 'admin_notices', [ $this, 'suppress_foreign_admin_notices' ], PHP_INT_MIN );
159 - add_action( 'all_admin_notices', [ $this, 'suppress_foreign_admin_notices' ], PHP_INT_MIN );
160 - add_action( 'network_admin_notices', [ $this, 'suppress_foreign_admin_notices' ], PHP_INT_MIN );
161 129 }
162 130
163 131 /**
164 - * Remove third-party admin notices on SureForms admin screens.
165 - *
166 - * Iterates over the callbacks registered on the admin notice hooks and
167 - * removes any that are not owned by SureForms. A callback is considered
168 - * owned by SureForms when it belongs to a class in the `SRFM` / `SRFM_PRO`
169 - * namespaces or to the bundled `BSF_Admin_Notices` / `Astra_Notices`
170 - * notices library. SureForms' own notices are therefore preserved while
171 - * foreign promotional banners are suppressed.
172 - *
173 - * This callback is hooked at `PHP_INT_MIN` so that it runs first on each
174 - * notice hook and removes the foreign callbacks before WordPress echoes
175 - * them (WP_Hook honours removals made during iteration). It is strictly
176 - * scoped to SureForms admin screens via {@see Helper::is_sureforms_admin_page()}
177 - * so no other admin page is affected.
178 - *
179 - * @since 2.10.0
180 - * @return void
181 - */
182 - public function suppress_foreign_admin_notices() {
183 - // Bail early if we are not on a SureForms admin screen. This keeps the
184 - // suppression strictly scoped and avoids touching any other admin page.
185 - // is_sureforms_admin_page() covers the core screens (dashboard, add-new,
186 - // settings, entries, the form CPT); we additionally match any admin page
187 - // whose `page` slug is SureForms-owned (sureforms_* / srfm_*) so the
188 - // suppression also applies to the payments/quiz/survey/learn/SMTP screens.
189 - if ( ! Helper::is_sureforms_admin_page() && ! $this->is_sureforms_owned_admin_page() ) {
190 - return;
191 - }
192 -
193 - global $wp_filter;
194 -
195 - // The hook currently being fired (admin_notices, all_admin_notices or network_admin_notices).
196 - $current_hook = current_action();
197 -
198 - if ( empty( $current_hook ) || empty( $wp_filter[ $current_hook ] ) || ! ( $wp_filter[ $current_hook ] instanceof \WP_Hook ) ) {
199 - return;
200 - }
201 -
202 - foreach ( $wp_filter[ $current_hook ]->callbacks as $priority => $callbacks ) {
203 - foreach ( $callbacks as $callback ) {
204 - $function = $callback['function'] ?? null;
205 -
206 - // Never remove our own suppression callback.
207 - if ( is_array( $function ) && isset( $function[0] ) && $function[0] === $this && 'suppress_foreign_admin_notices' === $function[1] ) {
208 - continue;
209 - }
210 -
211 - // Preserve SureForms-owned notices, remove everything else.
212 - if ( $this->is_sureforms_owned_notice_callback( $function ) ) {
213 - continue;
214 - }
215 -
216 - remove_action( $current_hook, $function, $priority );
217 - }
218 - }
219 - }
220 -
221 - /**
222 132 * Get the first form creation time stamp.
223 133 *
224 134 * @since 1.10.1
225 135 * @return int|false
@@ -320,9 +230,14 @@
320 230 */
321 231 public function add_action_links( $links ) {
322 232 if ( ! Helper::has_pro() ) {
323 233 // Display upsell link if SureForms Pro is not installed.
324 - $upsell_link = Helper::get_sureforms_website_url( 'pricing', [ 'utm_medium' => 'plugin-list' ] );
234 + $upsell_link = add_query_arg(
235 + [
236 + 'utm_medium' => 'plugin-list',
237 + ],
238 + Helper::get_sureforms_website_url( 'pricing' )
239 + );
325 240
326 241 ob_start();
327 242 ?>
328 243 <a href="<?php echo esc_url( $upsell_link ); ?>" target="_blank" rel="noreferrer" class="sureforms-plugins-go-pro">
@@ -482,9 +397,14 @@
482 397 */
483 398 public function add_upgrade_to_pro() {
484 399 // The url used here is used as a selector for css to style the upgrade to pro submenu.
485 400 // If you are changing this url, please make sure to update the css as well.
486 - $upgrade_url = Helper::get_sureforms_website_url( 'upgrade', [ 'utm_medium' => 'submenu_link_upgrade' ] );
401 + $upgrade_url = add_query_arg(
402 + [
403 + 'utm_medium' => 'submenu_link_upgrade',
404 + ],
405 + Helper::get_sureforms_website_url( 'upgrade' )
406 + );
487 407
488 408 add_submenu_page(
489 409 'sureforms_menu',
490 410 __( 'Upgrade', 'sureforms' ),
@@ -494,107 +414,8 @@
494 414 );
495 415 }
496 416
497 417 /**
498 - * Add Quiz empty state submenu page for free users.
499 - *
500 - * @return void
501 - * @since 2.7.0
502 - */
503 - public function add_quiz_page() {
504 - add_submenu_page(
505 - 'sureforms_menu',
506 - __( 'Quiz Entries', 'sureforms' ),
507 - __( 'Quizzes', 'sureforms' ) .
508 - ' <span style="color:#4ADE80;font-size:9px;font-weight:600;">' .
509 - esc_html__( 'New', 'sureforms' ) .
510 - '</span>',
511 - self::$sureforms_page_default_capability,
512 - 'sureforms_quiz_entries',
513 - [ $this, 'render_quiz_empty_state' ],
514 - 5
515 - );
516 - }
517 -
518 - /**
519 - * Quiz empty state page callback.
520 - *
521 - * @return void
522 - * @since 2.7.0
523 - */
524 - public function render_quiz_empty_state() {
525 - ?>
526 - <div id="srfm-quiz-entries-root" class="srfm-admin-wrapper"></div>
527 - <?php
528 - }
529 -
530 - /**
531 - * Add Survey Reports promotional submenu page for free users.
532 - *
533 - * @return void
534 - * @since 2.8.0
535 - */
536 - public function add_survey_reports_page() {
537 - add_submenu_page(
538 - 'sureforms_menu',
539 - __( 'Survey Reports', 'sureforms' ),
540 - __( 'Survey Reports', 'sureforms' ) .
541 - ' <span style="color:#4ADE80;font-size:9px;font-weight:600;">' .
542 - esc_html__( 'New', 'sureforms' ) .
543 - '</span>',
544 - self::$sureforms_page_default_capability,
545 - 'sureforms_survey_reports',
546 - [ $this, 'render_survey_empty_state' ],
547 - 6
548 - );
549 - }
550 -
551 - /**
552 - * Survey empty state page callback.
553 - *
554 - * @return void
555 - * @since 2.8.0
556 - */
557 - public function render_survey_empty_state() {
558 - ?>
559 - <div id="srfm-survey-empty-state-root" class="srfm-admin-wrapper"></div>
560 - <?php
561 - }
562 -
563 - /**
564 - * Add Partial Entries promotional submenu page for free users.
565 - *
566 - * @return void
567 - * @since 2.9.0
568 - */
569 - public function add_partial_entries_page() {
570 - add_submenu_page(
571 - 'sureforms_menu',
572 - __( 'Partial Entries', 'sureforms' ),
573 - __( 'Partial Entries', 'sureforms' ) .
574 - ' <span style="color:#4ADE80;font-size:9px;font-weight:600;">' .
575 - esc_html__( 'New', 'sureforms' ) .
576 - '</span>',
577 - self::$sureforms_page_default_capability,
578 - 'sureforms_partial_entries',
579 - [ $this, 'render_partial_entries_empty_state' ],
580 - 7
581 - );
582 - }
583 -
584 - /**
585 - * Partial Entries empty state page callback.
586 - *
587 - * @return void
588 - * @since 2.9.0
589 - */
590 - public function render_partial_entries_empty_state() {
591 - ?>
592 - <div id="srfm-partial-entries-empty-state-root" class="srfm-admin-wrapper"></div>
593 - <?php
594 - }
595 -
596 - /**
597 418 * Add SMTP promotional submenu page.
598 419 *
599 420 * @return void
600 421 * @since 1.7.1
@@ -840,8 +661,36 @@
840 661 }
841 662 }
842 663
843 664 /**
665 + * Summary of add_payments_badge
666 + *
667 + * @since 2.0.0
668 + * @return void
669 + */
670 + public function add_payments_badge() {
671 + if ( ! Helper::current_user_can() ) {
672 + return;
673 + }
674 +
675 + global $submenu;
676 + if ( isset( $submenu['sureforms_menu'] ) ) {
677 + foreach ( $submenu['sureforms_menu'] as $index => $sub_item ) {
678 + if ( isset( $sub_item[2] ) && SRFM_PAYMENTS === $sub_item[2] ) {
679 + ob_start();
680 + ?>
681 + <span style="color: #4ADE80;font-size: 9px;font-weight: 600;"><?php echo esc_html__( 'New', 'sureforms' ); ?></span>
682 + <?php
683 + $new_badge_html = ob_get_clean();
684 + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Adding notifications for submenu item.
685 + $submenu['sureforms_menu'][ $index ][0] .= $new_badge_html;
686 + break;
687 + }
688 + }
689 + }
690 + }
691 +
692 + /**
844 693 * Mark the user's visit to the entries page.
845 694 *
846 695 * @since 1.7.3
847 696 * @return void
@@ -908,9 +757,8 @@
908 757 wp_enqueue_style( SRFM_SLUG . '-backend-blocks', $css_uri . 'blocks/default/backend' . $file_prefix . '.css', [], SRFM_VER );
909 758 wp_enqueue_style( SRFM_SLUG . '-intl', $vendor_css_uri . 'intl/intlTelInput-backend.min.css', [], SRFM_VER );
910 759 wp_enqueue_style( SRFM_SLUG . '-common', $css_uri . 'common' . $file_prefix . '.css', [], SRFM_VER );
911 760 wp_enqueue_style( SRFM_SLUG . '-reactQuill', $vendor_css_uri . 'quill/quill.snow.css', [], SRFM_VER );
912 - wp_add_inline_style( SRFM_SLUG . '-reactQuill', self::QUILL_1X_INLINE_CSS );
913 761 wp_enqueue_style( SRFM_SLUG . '-single-form-modal', $css_uri . 'single-form-setting' . $file_prefix . '.css', [], SRFM_VER );
914 762
915 763 // if version is equal to or lower than 6.6.2 then add compatibility css.
916 764 if ( version_compare( $wp_version, '6.6.2', '<=' ) ) {
@@ -998,59 +846,40 @@
998 846 * List of the handles in which we need to add translation compatibility.
999 847 */
1000 848 $script_translations_handlers = [];
1001 849 $onboarding_instance = Onboarding::get_instance();
1002 - $current_user = wp_get_current_user();
1003 850
1004 851 $localization_data = [
1005 - 'site_url' => get_site_url(),
1006 - 'current_user_login' => $current_user->user_login ?? '',
1007 - 'website_lead_details' => [
1008 - 'first_name' => $current_user->first_name ?? '',
1009 - 'last_name' => $current_user->last_name ?? '',
1010 - 'email' => $current_user->user_email ?? '',
1011 - ],
1012 - 'breadcrumbs' => $this->get_breadcrumbs_for_current_page(),
1013 - 'sureforms_dashboard_url' => admin_url( '/admin.php?page=sureforms_menu' ),
1014 - 'plugin_version' => SRFM_VER,
1015 - 'global_settings_nonce' => Helper::current_user_can() ? wp_create_nonce( 'wp_rest' ) : '',
1016 - 'is_pro_active' => Helper::has_pro(),
1017 - 'is_first_form_created' => self::is_first_form_created(),
1018 - 'check_three_days_threshold' => self::check_first_form_creation_threshold(),
1019 - 'check_eight_days_threshold' => self::check_first_form_creation_threshold( 8 ),
1020 - 'pro_plugin_version' => Helper::has_pro() ? SRFM_PRO_VER : '',
1021 - 'pro_plugin_name' => Helper::has_pro() && defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'SureForms Pro',
1022 - 'sureforms_pricing_page' => Helper::get_sureforms_website_url( 'pricing' ),
1023 - 'field_spacing_vars' => Helper::get_css_vars(),
1024 - 'is_ver_lower_than_6_7' => version_compare( $wp_version, '6.6.2', '<=' ),
1025 - 'integrations' => Helper::sureforms_get_integration(),
1026 - 'rotating_plugin_banner' => Helper::get_rotating_plugin_banner(),
1027 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
1028 - 'sf_plugin_manager_nonce' => wp_create_nonce( 'sf_plugin_manager_nonce' ),
1029 - 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
1030 - 'plugin_activating_text' => __( 'Activating...', 'sureforms' ),
1031 - 'plugin_activated_text' => __( 'Activated', 'sureforms' ),
1032 - 'plugin_activate_text' => __( 'Activate', 'sureforms' ),
1033 - 'plugin_installing_text' => __( 'Installing...', 'sureforms' ),
1034 - 'plugin_installed_text' => __( 'Installed', 'sureforms' ),
1035 - 'privacy_policy_url' => Helper::get_sureforms_website_url( 'privacy-policy/' ),
1036 - 'is_rtl' => $is_rtl,
1037 - 'onboarding_completed' => method_exists( $onboarding_instance, 'get_onboarding_status' ) ? $onboarding_instance->get_onboarding_status() : false,
1038 - 'migration_banner_dismissed' => method_exists( $onboarding_instance, 'is_migration_banner_dismissed' ) ? $onboarding_instance->is_migration_banner_dismissed() : false,
1039 - 'migration_settings_url' => admin_url( 'admin.php?page=sureforms_form_settings&tab=migration-settings' ),
1040 - 'onboarding_redirect' => isset( $_GET['srfm-activation-redirect'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required for the activation redirection.
1041 - 'pointer_nonce' => wp_create_nonce( 'sureforms_pointer_action' ),
1042 - 'general_settings_url' => admin_url( '/options-general.php' ),
1043 - 'additional_header_nav_items' => [],
1044 - // Smart tags for the Global Defaults email notification fields.
1045 - // srfm_block_data is only available in the block editor; these keys
1046 - // make the same data accessible on the settings page via srfm_admin.
1047 - 'smart_tags_array' => Smart_Tags::smart_tag_list(),
1048 - 'smart_tags_array_email' => Smart_Tags::email_smart_tag_list(),
1049 - // Default confirmation message HTML (icon + heading + text) used as
1050 - // the initial React state before the settings API response arrives.
1051 - 'default_confirmation_message' => Global_Settings::get_default_confirmation_message(),
1052 - 'payments' => apply_filters(
852 + 'site_url' => get_site_url(),
853 + 'breadcrumbs' => $this->get_breadcrumbs_for_current_page(),
854 + 'sureforms_dashboard_url' => admin_url( '/admin.php?page=sureforms_menu' ),
855 + 'plugin_version' => SRFM_VER,
856 + 'global_settings_nonce' => Helper::current_user_can() ? wp_create_nonce( 'wp_rest' ) : '',
857 + 'is_pro_active' => Helper::has_pro(),
858 + 'is_first_form_created' => self::is_first_form_created(),
859 + 'check_three_days_threshold' => self::check_first_form_creation_threshold(),
860 + 'check_eight_days_threshold' => self::check_first_form_creation_threshold( 8 ),
861 + 'pro_plugin_version' => Helper::has_pro() ? SRFM_PRO_VER : '',
862 + 'pro_plugin_name' => Helper::has_pro() && defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'SureForms Pro',
863 + 'sureforms_pricing_page' => Helper::get_sureforms_website_url( 'pricing' ),
864 + 'field_spacing_vars' => Helper::get_css_vars(),
865 + 'is_ver_lower_than_6_7' => version_compare( $wp_version, '6.6.2', '<=' ),
866 + 'integrations' => Helper::sureforms_get_integration(),
867 + 'rotating_plugin_banner' => Helper::get_rotating_plugin_banner(),
868 + 'ajax_url' => admin_url( 'admin-ajax.php' ),
869 + 'sf_plugin_manager_nonce' => wp_create_nonce( 'sf_plugin_manager_nonce' ),
870 + 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
871 + 'plugin_activating_text' => __( 'Activating...', 'sureforms' ),
872 + 'plugin_activated_text' => __( 'Activated', 'sureforms' ),
873 + 'plugin_activate_text' => __( 'Activate', 'sureforms' ),
874 + 'plugin_installing_text' => __( 'Installing...', 'sureforms' ),
875 + 'plugin_installed_text' => __( 'Installed', 'sureforms' ),
876 + 'is_rtl' => $is_rtl,
877 + 'onboarding_completed' => method_exists( $onboarding_instance, 'get_onboarding_status' ) ? $onboarding_instance->get_onboarding_status() : false,
878 + 'onboarding_redirect' => isset( $_GET['srfm-activation-redirect'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required for the activation redirection.
879 + 'pointer_nonce' => wp_create_nonce( 'sureforms_pointer_action' ),
880 + 'general_settings_url' => admin_url( '/options-general.php' ),
881 + 'payments' => apply_filters(
1053 882 'srfm_admin_localize_payments_data',
1054 883 [
1055 884 'stripe_connected' => Stripe_Helper::is_stripe_connected(),
1056 885 'stripe_mode' => Stripe_Helper::get_stripe_mode(),
@@ -1064,25 +893,18 @@
1064 893 'payment_currency' => Payment_Helper::get_currency(),
1065 894 'currency_sign_position' => Payment_Helper::get_currency_sign_position(),
1066 895 ]
1067 896 ),
1068 - 'mcp_adapter_status' => file_exists( WP_PLUGIN_DIR . '/mcp-adapter/mcp-adapter.php' )
1069 - ? ( is_plugin_active( 'mcp-adapter/mcp-adapter.php' ) ? 'active' : 'installed' )
1070 - : 'not_installed',
1071 - 'mcp_endpoint_url' => esc_url_raw( rest_url( 'sureforms/v1/mcp' ) ),
1072 897 ];
1073 898
1074 - $is_screen_sureforms_menu = Helper::validate_request_context( 'sureforms_menu', 'page' );
1075 - $is_screen_add_new_form = Helper::validate_request_context( 'add-new-form', 'page' );
1076 - $is_screen_sureforms_forms = Helper::validate_request_context( 'sureforms_forms', 'page' );
1077 - $is_screen_sureforms_form_settings = Helper::validate_request_context( 'sureforms_form_settings', 'page' );
1078 - $is_screen_sureforms_payments = Helper::validate_request_context( 'sureforms_payments', 'page' );
1079 - $is_screen_sureforms_entries = Helper::validate_request_context( SRFM_ENTRIES, 'page' );
1080 - $is_screen_sureforms_learn = Helper::validate_request_context( 'sureforms_learn', 'page' );
1081 - $is_screen_quiz_empty_state = Helper::validate_request_context( 'sureforms_quiz_entries', 'page' );
1082 - $is_screen_survey_empty_state = Helper::validate_request_context( 'sureforms_survey_reports', 'page' );
1083 - $is_screen_partial_entries_empty_state = Helper::validate_request_context( 'sureforms_partial_entries', 'page' );
1084 - $is_post_type_sureforms_form = SRFM_FORMS_POST_TYPE === $current_screen->post_type;
899 + $is_screen_sureforms_menu = Helper::validate_request_context( 'sureforms_menu', 'page' );
900 + $is_screen_add_new_form = Helper::validate_request_context( 'add-new-form', 'page' );
901 + $is_screen_sureforms_forms = Helper::validate_request_context( 'sureforms_forms', 'page' );
902 + $is_screen_sureforms_form_settings = Helper::validate_request_context( 'sureforms_form_settings', 'page' );
903 + $is_screen_sureforms_payments = Helper::validate_request_context( 'sureforms_payments', 'page' );
904 + $is_screen_sureforms_entries = Helper::validate_request_context( SRFM_ENTRIES, 'page' );
905 + $is_screen_sureforms_learn = Helper::validate_request_context( 'sureforms_learn', 'page' );
906 + $is_post_type_sureforms_form = SRFM_FORMS_POST_TYPE === $current_screen->post_type;
1085 907
1086 908 /**
1087 909 * Check if the current screen is the SureForms Menu and AI Auth Email is present then we will add user type as registered.
1088 910 * Compatibility with existing UI code that checks for this condition.
@@ -1093,39 +915,9 @@
1093 915 'type' => ! empty( get_option( 'srfm_ai_auth_user_email' ) ) ? 'registered' : 'non-registered',
1094 916 ];
1095 917 }
1096 918
1097 - // Add the Quizzes and Survey Reports nav items when pro is not active.
1098 - if ( ! Helper::has_pro() ) {
1099 - $localization_data['additional_header_nav_items'][] = [
1100 - 'slug' => 'sureforms_quiz_entries',
1101 - 'text' => __( 'Quizzes', 'sureforms' ),
1102 - 'link' => admin_url( 'admin.php?page=sureforms_quiz_entries' ),
1103 - ];
1104 - $localization_data['additional_header_nav_items'][] = [
1105 - 'slug' => 'sureforms_survey_reports',
1106 - 'text' => __( 'Survey Reports', 'sureforms' ),
1107 - 'link' => admin_url( 'admin.php?page=sureforms_survey_reports' ),
1108 - ];
1109 - $localization_data['additional_header_nav_items'][] = [
1110 - 'slug' => 'sureforms_partial_entries',
1111 - 'text' => __( 'Partial Entries', 'sureforms' ),
1112 - 'link' => admin_url( 'admin.php?page=sureforms_partial_entries' ),
1113 - ];
1114 - }
1115 -
1116 - $is_sureforms_screen = $is_screen_sureforms_menu || $is_post_type_sureforms_form || $is_screen_add_new_form || $is_screen_sureforms_forms || $is_screen_sureforms_form_settings || $is_screen_sureforms_entries || $is_screen_sureforms_payments || $is_screen_sureforms_learn || $is_screen_quiz_empty_state || $is_screen_survey_empty_state || $is_screen_partial_entries_empty_state;
1117 -
1118 - /**
1119 - * Filter to allow extending the SureForms dashboard screen check.
1120 - *
1121 - * @since 2.6.0
1122 - *
1123 - * @param bool $is_sureforms_screen Whether the current screen is a SureForms dashboard screen.
1124 - */
1125 - $is_sureforms_screen = apply_filters( 'srfm_is_dashboard_screen', $is_sureforms_screen );
1126 -
1127 - if ( $is_sureforms_screen ) {
919 + if ( $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 ) {
1128 920 $asset_handle = '-dashboard';
1129 921
1130 922 wp_enqueue_style( SRFM_SLUG . $asset_handle . '-font', 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap', [], SRFM_VER );
1131 923
@@ -1268,62 +1060,8 @@
1268 1060
1269 1061 $script_translations_handlers[] = SRFM_SLUG . '-suremail';
1270 1062 }
1271 1063
1272 - // Enqueue scripts for the Quiz empty state page (free users only).
1273 - if ( $is_screen_quiz_empty_state && ! Helper::has_pro() ) {
1274 - $asset_handle = 'quizEmptyState';
1275 -
1276 - $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1277 - $script_info = file_exists( $script_asset_path )
1278 - ? include $script_asset_path
1279 - : [
1280 - 'dependencies' => [],
1281 - 'version' => SRFM_VER,
1282 - ];
1283 -
1284 - wp_enqueue_script( SRFM_SLUG . '-quiz-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1285 - wp_enqueue_style( SRFM_SLUG . '-quiz-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1286 -
1287 - $script_translations_handlers[] = SRFM_SLUG . '-quiz-empty-state';
1288 - }
1289 -
1290 - // Enqueue scripts for the Survey Reports empty state page (free users only).
1291 - if ( $is_screen_survey_empty_state && ! Helper::has_pro() ) {
1292 - $asset_handle = 'surveyEmptyState';
1293 -
1294 - $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1295 - $script_info = file_exists( $script_asset_path )
1296 - ? include $script_asset_path
1297 - : [
1298 - 'dependencies' => [],
1299 - 'version' => SRFM_VER,
1300 - ];
1301 -
1302 - wp_enqueue_script( SRFM_SLUG . '-survey-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1303 - wp_enqueue_style( SRFM_SLUG . '-survey-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1304 -
1305 - $script_translations_handlers[] = SRFM_SLUG . '-survey-empty-state';
1306 - }
1307 -
1308 - // Enqueue scripts for the Partial Entries empty state page (free users only).
1309 - if ( $is_screen_partial_entries_empty_state && ! Helper::has_pro() ) {
1310 - $asset_handle = 'partialEntriesEmptyState';
1311 -
1312 - $script_asset_path = SRFM_DIR . 'assets/build/' . $asset_handle . '.asset.php';
1313 - $script_info = file_exists( $script_asset_path )
1314 - ? include $script_asset_path
1315 - : [
1316 - 'dependencies' => [],
1317 - 'version' => SRFM_VER,
1318 - ];
1319 -
1320 - wp_enqueue_script( SRFM_SLUG . '-partial-entries-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1321 - wp_enqueue_style( SRFM_SLUG . '-partial-entries-empty-state', SRFM_URL . 'assets/build/' . $asset_handle . '.css', [], SRFM_VER, 'all' );
1322 -
1323 - $script_translations_handlers[] = SRFM_SLUG . '-partial-entries-empty-state';
1324 - }
1325 -
1326 1064 // Admin Submenu Styles.
1327 1065 wp_enqueue_style( SRFM_SLUG . '-admin', $css_uri . 'backend/admin' . $file_prefix . $rtl . '.css', [], SRFM_VER );
1328 1066
1329 1067 if ( $is_screen_sureforms_form_settings ) {
@@ -1340,19 +1078,11 @@
1340 1078 wp_enqueue_script( SRFM_SLUG . '-settings', SRFM_URL . 'assets/build/' . $asset_handle . '.js', $script_info['dependencies'], SRFM_VER, true );
1341 1079 wp_localize_script(
1342 1080 SRFM_SLUG . '-settings',
1343 1081 SRFM_SLUG . '_admin',
1344 - apply_filters(
1345 - SRFM_SLUG . '_admin_filter',
1346 - $localization_data
1347 - )
1082 + $localization_data
1348 1083 );
1349 1084
1350 - // Enqueue Tailwind and Quill editor styles for the settings page.
1351 - wp_enqueue_style( SRFM_SLUG . '-settings-build', SRFM_URL . 'assets/build/settings.css', [], SRFM_VER, 'all' );
1352 - wp_enqueue_style( SRFM_SLUG . '-reactQuill', SRFM_URL . 'assets/css/minified/deps/quill/quill.snow.css', [], SRFM_VER );
1353 - wp_add_inline_style( SRFM_SLUG . '-reactQuill', self::QUILL_1X_INLINE_CSS );
1354 -
1355 1085 $script_translations_handlers[] = SRFM_SLUG . '-settings';
1356 1086 }
1357 1087
1358 1088 if ( $is_screen_add_new_form ) {
@@ -1655,9 +1385,9 @@
1655 1385 'type' => '',
1656 1386 'message' => $this->build_notice_markup(
1657 1387 esc_html__( 'Amazing! SureForms is powering your forms and submissions - let\'s keep growing together!', 'sureforms' ),
1658 1388 esc_html__( 'If SureForms has been helpful, would you mind taking a moment to leave a 5-star review on WordPress.org?', 'sureforms' ),
1659 - esc_url( 'https://wordpress.org/support/plugin/sureforms/reviews/' ),
1389 + esc_url( 'https://wordpress.org/support/plugin/sureforms/reviews/?filter=5#new-post' ),
1660 1390 esc_html__( 'Rate SureForms', 'sureforms' ),
1661 1391 esc_html__( 'Maybe later', 'sureforms' ),
1662 1392 esc_html__( 'I already did', 'sureforms' ),
1663 1393 WEEK_IN_SECONDS,
@@ -1786,9 +1516,9 @@
1786 1516 wp_send_json_error( [ 'message' => __( 'Invalid parameters.', 'sureforms' ) ], 400 );
1787 1517 }
1788 1518
1789 1519 $event_name = $valid[ $notice_id ][ $button ];
1790 - Analytics::events()->track( $event_name, $button );
1520 + Analytics_Events::track( $event_name, $button );
1791 1521
1792 1522 wp_send_json_success();
1793 1523 }
1794 1524
@@ -1937,11 +1667,8 @@
1937 1667 if ( ! Helper::current_user_can() ) {
1938 1668 return;
1939 1669 }
1940 1670
1941 - // Register the AI quick draft widget for capable users (the capability gate above applies); unlike the recent-entries widget below, it is not conditional on having entries.
1942 - add_action( 'wp_dashboard_setup', [ $this, 'register_ai_dashboard_widget' ] );
1943 -
1944 1671 // Quick check if there are any entries in the last 7 days.
1945 1672 $seven_days_ago = strtotime( '-7 days' );
1946 1673 $total_entries = Entries::get_entries_count_after( $seven_days_ago );
1947 1674
@@ -1976,180 +1703,8 @@
1976 1703 );
1977 1704 }
1978 1705
1979 1706 /**
1980 - * Register the AI quick draft dashboard widget.
1981 - *
1982 - * @return void
1983 - * @since 2.12.1
1984 - */
1985 - public function register_ai_dashboard_widget() {
1986 - wp_add_dashboard_widget(
1987 - 'sureforms_ai_quick_draft',
1988 - __( 'SureForms AI Quick Draft', 'sureforms' ),
1989 - [ $this, 'render_ai_dashboard_widget' ],
1990 - null,
1991 - null,
1992 - 'normal',
1993 - 'high'
1994 - );
1995 - }
1996 -
1997 - /**
1998 - * Render AI quick draft dashboard widget content.
1999 - *
2000 - * @return void
2001 - * @since 2.12.1
2002 - */
2003 - public function render_ai_dashboard_widget() {
2004 - ?>
2005 - <div class="srfm-ai-dashboard-widget">
2006 - <p>
2007 - <?php esc_html_e( 'Describe the form and let SureForms AI generate it for you.', 'sureforms' ); ?>
2008 - </p>
2009 - <label for="srfm-ai-dashboard-prompt" class="screen-reader-text">
2010 - <?php esc_html_e( 'Describe your form', 'sureforms' ); ?>
2011 - </label>
2012 - <textarea
2013 - id="srfm-ai-dashboard-prompt"
2014 - class="widefat"
2015 - rows="5"
2016 - maxlength="2000"
2017 - placeholder="<?php esc_attr_e( 'Example: Create a contact form with name, email, phone, and message fields.', 'sureforms' ); ?>"
2018 - ></textarea>
2019 - <p style="margin-top:10px;margin-bottom:0;display:flex;align-items:center;gap:10px;">
2020 - <button type="button" class="button button-primary" id="srfm-ai-dashboard-generate" disabled>
2021 - <?php esc_html_e( 'Create New Form', 'sureforms' ); ?>
2022 - </button>
2023 - <span id="srfm-ai-dashboard-char-count" style="color:#646970;">0/2000</span>
2024 - </p>
2025 - </div>
2026 - <?php
2027 - }
2028 -
2029 - /**
2030 - * Enqueue the AI quick draft dashboard widget script on the dashboard screen.
2031 - *
2032 - * The widget's behavior lives here (attached via wp_add_inline_script) rather than as an
2033 - * inline <script> in the render callback, so it passes Plugin Check and keeps server values
2034 - * out of the markup. Server values are passed through wp_localize_script.
2035 - *
2036 - * @param string $hook_suffix The current admin page hook suffix.
2037 - * @return void
2038 - * @since 2.12.1
2039 - */
2040 - public function enqueue_ai_dashboard_widget_assets( $hook_suffix ) {
2041 - // Only on the main dashboard, and only for capable users (matches the widget gate).
2042 - if ( 'index.php' !== $hook_suffix || ! Helper::current_user_can() ) {
2043 - return;
2044 - }
2045 -
2046 - // Register an inline-only handle (empty src) — the WordPress-core pattern for attaching
2047 - // localized data plus an inline script without shipping a separate asset file.
2048 - wp_register_script( 'srfm-ai-dashboard-widget', '', [], SRFM_VER, true );
2049 - wp_enqueue_script( 'srfm-ai-dashboard-widget' );
2050 -
2051 - wp_localize_script(
2052 - 'srfm-ai-dashboard-widget',
2053 - 'srfmAiDashboardWidget',
2054 - [
2055 - 'redirectUrl' => admin_url( 'admin.php?page=add-new-form' ),
2056 - 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
2057 - 'nonce' => wp_create_nonce( 'srfm_ai_widget_usage' ),
2058 - 'redirectingTxt' => __( 'Redirecting...', 'sureforms' ),
2059 - ]
2060 - );
2061 -
2062 - $inline_script = <<<'JS'
2063 -( function () {
2064 - const config = window.srfmAiDashboardWidget || {};
2065 - const generateButton = document.getElementById( 'srfm-ai-dashboard-generate' );
2066 - const promptField = document.getElementById( 'srfm-ai-dashboard-prompt' );
2067 - const charCount = document.getElementById( 'srfm-ai-dashboard-char-count' );
2068 - if ( ! generateButton || ! promptField ) {
2069 - return;
2070 - }
2071 -
2072 - const updateWidgetState = function () {
2073 - const promptValue = promptField.value.trim();
2074 - generateButton.disabled = ! promptValue;
2075 - if ( charCount ) {
2076 - charCount.textContent = `${ promptField.value.length }/2000`;
2077 - }
2078 - };
2079 -
2080 - const triggerGeneration = function () {
2081 - const prompt = promptField.value.trim();
2082 - if ( ! prompt ) {
2083 - promptField.focus();
2084 - return;
2085 - }
2086 -
2087 - generateButton.disabled = true;
2088 - generateButton.textContent = config.redirectingTxt;
2089 -
2090 - const redirectUrl = new URL( config.redirectUrl, window.location.origin );
2091 - redirectUrl.searchParams.set( 'srfm_ai_dashboard_prompt', prompt );
2092 -
2093 - const requestBody = new URLSearchParams();
2094 - requestBody.append( 'action', 'srfm_ai_widget_usage' );
2095 - requestBody.append( 'nonce', config.nonce );
2096 -
2097 - fetch( config.ajaxUrl, {
2098 - method: 'POST',
2099 - credentials: 'same-origin',
2100 - headers: {
2101 - 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
2102 - },
2103 - body: requestBody.toString(),
2104 - } ).finally( function () {
2105 - window.location.href = redirectUrl.toString();
2106 - } );
2107 - };
2108 -
2109 - promptField.addEventListener( 'input', updateWidgetState );
2110 - generateButton.addEventListener( 'click', triggerGeneration );
2111 - promptField.addEventListener( 'keydown', function ( event ) {
2112 - if ( event.key === 'Enter' && ( event.metaKey || event.ctrlKey ) ) {
2113 - event.preventDefault();
2114 - triggerGeneration();
2115 - }
2116 - } );
2117 -
2118 - updateWidgetState();
2119 -}() );
2120 -JS;
2121 -
2122 - wp_add_inline_script( 'srfm-ai-dashboard-widget', $inline_script );
2123 - }
2124 -
2125 - /**
2126 - * Track AI dashboard widget usage.
2127 - *
2128 - * @return void
2129 - * @since 2.12.1
2130 - */
2131 - public function track_ai_widget_usage() {
2132 - if ( ! check_ajax_referer( 'srfm_ai_widget_usage', 'nonce', false ) ) {
2133 - wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ], 403 );
2134 - }
2135 -
2136 - if ( ! Helper::current_user_can() ) {
2137 - wp_send_json_error( [ 'message' => __( 'Unauthorized user.', 'sureforms' ) ], 403 );
2138 - }
2139 -
2140 - $current_count = (int) Helper::get_srfm_option( 'ai_dashboard_widget_uses', 0 ) + 1;
2141 - Helper::update_srfm_option( 'ai_dashboard_widget_uses', $current_count );
2142 -
2143 - // Emit an analytics event so usage lands in the warehouse via events_record.
2144 - // $force = true because this is a cumulative counter, not a one-time event —
2145 - // it must re-send the latest count each cycle (bypasses one-time dedup).
2146 - Analytics::events()->track( 'ai_dashboard_widget_used', (string) $current_count, [], true );
2147 -
2148 - wp_send_json_success();
2149 - }
2150 -
2151 - /**
2152 1707 * Render the dashboard widget content.
2153 1708 *
2154 1709 * @return void
2155 1710 * @since 1.9.1
@@ -2198,65 +1753,8 @@
2198 1753 <?php
2199 1754 }
2200 1755
2201 1756 /**
2202 - * Determine whether a notice callback is owned by SureForms.
2203 - *
2204 - * Recognises object methods on classes in the `SRFM` / `SRFM_PRO` namespaces
2205 - * as well as the bundled notices libraries (`BSF_Admin_Notices` and the
2206 - * legacy `Astra_Notices` alias). Everything else is treated as foreign.
2207 - *
2208 - * @param callable|array|string|null $function The registered callback function.
2209 - * @since 2.10.0
2210 - * @return bool True when the callback belongs to SureForms, false otherwise.
2211 - */
2212 - private function is_sureforms_owned_notice_callback( $function ) {
2213 - $class_name = '';
2214 -
2215 - if ( is_array( $function ) && isset( $function[0] ) ) {
2216 - // Object or static method callback represented as an array. The first
2217 - // element is either the object instance or the fully qualified class name.
2218 - $class_name = is_object( $function[0] ) ? get_class( $function[0] ) : (string) $function[0];
2219 - } elseif ( is_string( $function ) && false !== strpos( $function, '::' ) ) {
2220 - // Static method passed as "Class::method".
2221 - $class_name = strstr( $function, '::', true );
2222 - }
2223 -
2224 - if ( '' === $class_name ) {
2225 - // Plain function callbacks are never owned by SureForms.
2226 - return false;
2227 - }
2228 -
2229 - // SureForms (free and pro) namespaced classes. Pro's real namespace is
2230 - // `SRFM_Pro\` (case-sensitive) — not the all-caps `SRFM_PRO_` constant
2231 - // prefix — so match it case-insensitively to be safe.
2232 - if ( 0 === strpos( $class_name, 'SRFM\\' ) || 0 === stripos( $class_name, 'SRFM_Pro\\' ) ) {
2233 - return true;
2234 - }
2235 -
2236 - // Bundled notices library shipped with SureForms.
2237 - return in_array( $class_name, [ 'BSF_Admin_Notices', 'Astra_Notices' ], true );
2238 - }
2239 -
2240 - /**
2241 - * Whether the current admin page is a SureForms-owned screen identified by a
2242 - * `sureforms_*` / `srfm_*` `page` query slug. Complements
2243 - * {@see Helper::is_sureforms_admin_page()} so foreign-notice suppression also
2244 - * covers the payments / quiz / survey / learn / SMTP / partial-entries screens
2245 - * that the core helper does not enumerate. Read-only screen check.
2246 - *
2247 - * @since 2.10.0
2248 - * @return bool
2249 - */
2250 - private function is_sureforms_owned_admin_page() {
2251 - if ( ! is_admin() || empty( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection, no state change.
2252 - return false;
2253 - }
2254 - $page = sanitize_key( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection, no state change.
2255 - return 0 === strpos( $page, 'sureforms' ) || 0 === strpos( $page, 'srfm' );
2256 - }
2257 -
2258 - /**
2259 1757 * Build the shared HTML markup for admin notices.
2260 1758 *
2261 1759 * @since 2.5.2
2262 1760 *
@@ -2396,9 +1894,14 @@
2396 1894 </svg>
2397 1895 <span><?php echo esc_html( $this->get_random_premium_feature_text() ); ?></span>
2398 1896 </div>
2399 1897 <?php
2400 - $upgrade_url = Helper::get_sureforms_website_url( 'pricing', [ 'utm_medium' => 'dashboard-widget' ] );
1898 + $upgrade_url = add_query_arg(
1899 + [
1900 + 'utm_medium' => 'dashboard-widget',
1901 + ],
1902 + Helper::get_sureforms_website_url( 'pricing' )
1903 + );
2401 1904 ?>
2402 1905 <a href="<?php echo esc_url( $upgrade_url ); ?>" class="srfm-upgrade-link" target="_blank">
2403 1906 <?php esc_html_e( 'Upgrade', 'sureforms' ); ?>
2404 1907 </a>