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