PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.6.1
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.6.1
1.6.1 1.6.0 1.5.1 1.5.0 1.4.0 1.3.0 trunk 0.0.1 1.0.0 1.1.0 1.1.1 1.1.2 1.2.0
← All changes | inc/admin/admin.php +178 -19 1.1.0 → 1.6.1 View file →
@@ -6,13 +6,16 @@
6 6 */
7 7
8 8 namespace SureDonation\Inc\Admin;
9 9
10 +use SureDonation\Inc\Campaigns\Campaign_Cpt;
11 +use SureDonation\Inc\API\Onboarding_API;
10 12 use SureDonation\Inc\Helper;
11 13 use SureDonation\Inc\Onboarding;
12 14 use SureDonation\Inc\Pdf\Pdf_Utils;
13 15 use SureDonation\Inc\Payments\Payment_Helper;
14 16 use SureDonation\Inc\Payments\PayPal\PayPal_Helper;
17 +use SureDonation\Inc\Payments\Stripe\Stripe_Helper;
15 18 use SureDonation\Inc\Traits\Get_Instance;
16 19
17 20 // Exit if accessed directly.
18 21 if ( ! defined( 'ABSPATH' ) ) {
@@ -36,11 +39,114 @@
36 39 add_action( 'admin_menu', [ $this, 'register_menu' ] );
37 40 add_action( 'admin_menu', [ $this, 'hide_forms_submenu' ], 999 );
38 41 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] );
39 42 add_action( 'admin_init', [ $this, 'redirect_cpt_listings' ] );
43 + add_action( 'admin_init', [ $this, 'register_privacy_policy_content' ] );
44 + add_action( 'admin_init', [ $this, 'register_react_notices' ], 5 );
40 45 }
41 46
42 47 /**
48 + * Register the payment-gateway React admin notice.
49 + *
50 + * Bridges the payment/test-mode nudges into the React admin app via
51 + * Notice_Manager. Runs on admin_init (priority 5) so the notice is
52 + * registered before admin_enqueue_scripts localizes the data for React.
53 + *
54 + * Two mutually-exclusive states, mirroring the existing PHP/front-end
55 + * notice chain:
56 + * - No gateway connected -> prompt to configure a gateway.
57 + * - Connected, but site in test mode -> prompt to switch to live mode.
58 + * When a gateway is connected and the site is live, no notice shows.
59 + *
60 + * Capability-gated to manage_options; the data is only consumed on the
61 + * SureDonation admin pages, where the React app is enqueued.
62 + *
63 + * Hooked - admin_init (priority 5)
64 + *
65 + * @since 1.3.0
66 + * @return void
67 + */
68 + public function register_react_notices() {
69 + if ( ! current_user_can( 'manage_options' ) ) {
70 + return;
71 + }
72 +
73 + if ( ! Payment_Helper::is_any_gateway_connected() ) {
74 + Notice_Manager::register_notice(
75 + [
76 + 'id' => 'sd-configure-gateway',
77 + 'variant' => 'error',
78 + // translators: the <a></a> tags wrap the inline link text and must be kept intact.
79 + 'message' => __( 'Your forms cannot accept donations yet. <a>Connect Stripe or PayPal</a> and you can start accepting them today.', 'suredonation' ),
80 + 'link' => [
81 + // Deep-link to the gateway connect screen, matching the dashboard Quick Access entry.
82 + 'url' => Payment_Helper::get_settings_url( 'stripe' ),
83 + ],
84 + 'event' => Analytics::TRACKED_EVENTS['configure_gateway'],
85 + 'dismissible' => false,
86 + ]
87 + );
88 + return;
89 + }
90 +
91 + if ( Stripe_Helper::is_stripe_connected() && ! Stripe_Helper::is_webhook_configured() ) {
92 + Notice_Manager::register_notice(
93 + [
94 + 'id' => 'sd-webhook-not-configured',
95 + 'variant' => 'error',
96 + // translators: the <a></a> tags wrap the inline link text and must be kept intact.
97 + 'message' => __( 'Webhooks keep SureDonation in sync with Stripe by automatically updating donation and subscription data. Please <a>configure</a> the webhook.', 'suredonation' ),
98 + 'link' => [
99 + 'url' => Payment_Helper::get_settings_url( 'stripe' ),
100 + ],
101 + 'event' => Analytics::TRACKED_EVENTS['webhook'],
102 + 'dismissible' => false,
103 + ]
104 + );
105 + return;
106 + }
107 +
108 + if ( 'test' === Payment_Helper::get_payment_mode() ) {
109 + $settings_url = Payment_Helper::get_settings_url();
110 + Notice_Manager::register_notice(
111 + [
112 + 'id' => 'sd-test-mode-react',
113 + 'variant' => 'error',
114 + // translators: the <a></a> tags wrap the inline link text and must be kept intact.
115 + 'message' => __( 'Supporters cannot donate while your site is in test mode. Anything they try now is a test and no money reaches you. <a>Switch to live mode</a> when you are ready to accept real donations.', 'suredonation' ),
116 + 'link' => [
117 + 'url' => $settings_url,
118 + ],
119 + 'event' => Analytics::TRACKED_EVENTS['test_mode'],
120 + 'dismissible' => false,
121 + ]
122 + );
123 + }
124 + }
125 +
126 + /**
127 + * Register suggested privacy-policy text for the plugin's data transfers.
128 + *
129 + * Surfaces the Phone field's automatic country detection (which sends the
130 + * visitor's IP to ipapi.co) in the WordPress Privacy Policy Guide
131 + * (Settings → Privacy → Policy Guide) so site owners can disclose it in
132 + * their own policy. This only suggests text to admins — it publishes
133 + * nothing and is not shown to donors.
134 + *
135 + * @since 1.1.1
136 + * @return void
137 + */
138 + public function register_privacy_policy_content() {
139 + if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
140 + return;
141 + }
142 +
143 + $content = __( 'When a Phone field with automatic country detection is used, this site sends the visitor’s IP address to a third-party service (ipapi.co) to determine their country code. The IP address is not stored for this purpose; the resulting country code is cached temporarily. Automatic detection can be disabled per Phone field, or site-wide via the “suredonation_phone_geo_enabled” filter.', 'suredonation' );
144 +
145 + wp_add_privacy_policy_content( 'SureDonation', wp_kses_post( wpautop( $content ) ) );
146 + }
147 +
148 + /**
43 149 * Redirect the default CPT list screens for suredonation_form and
44 150 * suredonation_cmpgn to their corresponding plugin pages.
45 151 *
46 152 * For the form listing, attempt to resolve the originating campaign
@@ -46,8 +152,13 @@
46 152 * For the form listing, attempt to resolve the originating campaign
47 153 * from the referer (the "back" arrow on the form editor sends the
48 154 * user here) and redirect to that campaign's single view.
49 155 *
156 + * Campaign creation (post-new.php) is redirected too, so bookmarks and
157 + * third-party links to the raw editor land in the creation drawer rather
158 + * than producing a half-configured campaign. New forms keep using
159 + * post-new.php — the campaign screen links there with a campaign_id.
160 + *
50 161 * @return void
51 162 * @since 1.0.0
52 163 */
53 164 public function redirect_cpt_listings() {
@@ -52,9 +163,9 @@
52 163 */
53 164 public function redirect_cpt_listings() {
54 165 global $pagenow;
55 166
56 - if ( 'edit.php' !== $pagenow ) {
167 + if ( 'edit.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
57 168 return;
58 169 }
59 170
60 171 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading post_type from query, no state mutation.
@@ -59,8 +170,22 @@
59 170
60 171 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading post_type from query, no state mutation.
61 172 $post_type = isset( $_GET['post_type'] ) ? sanitize_key( wp_unslash( $_GET['post_type'] ) ) : '';
62 173
174 + if ( 'post-new.php' === $pagenow ) {
175 + /*
176 + * Only for users who can actually open the destination: the admin app
177 + * requires manage_options, while the campaign post type is creatable
178 + * by any role with edit_posts. Redirecting those roles would trade the
179 + * editor for a permission error.
180 + */
181 + if ( Campaign_Cpt::POST_TYPE === $post_type && current_user_can( 'manage_options' ) ) {
182 + wp_safe_redirect( Campaign_Cpt::get_create_url() );
183 + exit;
184 + }
185 + return;
186 + }
187 +
63 188 if ( 'suredonation_form' === $post_type ) {
64 189 $campaign_id = $this->resolve_campaign_id_from_referer();
65 190 $target = $campaign_id
66 191 ? admin_url( 'admin.php?page=suredonation#/campaigns/' . $campaign_id )
@@ -69,9 +194,9 @@
69 194 wp_safe_redirect( $target );
70 195 exit;
71 196 }
72 197
73 - if ( 'suredonation_cmpgn' === $post_type ) {
198 + if ( Campaign_Cpt::POST_TYPE === $post_type ) {
74 199 wp_safe_redirect( admin_url( 'admin.php?page=suredonation#/campaigns' ) );
75 200 exit;
76 201 }
77 202 }
@@ -177,12 +302,27 @@
177 302
178 303 // Load JS translations for the admin app.
179 304 wp_set_script_translations( 'suredonation-admin', 'suredonation' );
180 305
306 + // Campaign guided tour resume point (empty when there is none).
307 + $campaign_tour_progress = get_user_meta( get_current_user_id(), Onboarding_API::TOUR_PROGRESS_META, true );
308 + $campaign_tour_progress = is_string( $campaign_tour_progress ) ? $campaign_tour_progress : '';
309 +
310 + // Campaign whose first-run tour the onboarding wizard armed (empty = none).
311 + $campaign_tour_pending = get_user_meta( get_current_user_id(), Onboarding_API::TOUR_PENDING_META, true );
312 + $campaign_tour_pending = is_scalar( $campaign_tour_pending ) ? (string) $campaign_tour_pending : '';
313 +
181 314 // Localize script with plugin data.
182 - wp_localize_script(
183 - 'suredonation-admin',
184 - 'suredonation_admin',
315 + /**
316 + * Filters the bootstrap data localized to the SureDonation admin React app
317 + * (exposed as window.suredonation_admin). Lets features inject additional
318 + * data before it reaches React.
319 + *
320 + * @since 1.3.0
321 + * @param array<string, mixed> $localized_data The admin app bootstrap data.
322 + */
323 + $localized_data = apply_filters(
324 + 'suredonation_admin_app_data',
185 325 [
186 326 'version' => SUREDONATION_VER,
187 327 'apiUrl' => rest_url( 'suredonation/v1' ),
188 328 'nonce' => wp_create_nonce( 'wp_rest' ),
@@ -191,8 +331,11 @@
191 331 'site_url' => site_url(),
192 332 'admin_url' => admin_url(),
193 333 'is_first_campaign_created' => $this->has_campaigns(),
194 334 'is_onboarding_completed' => Onboarding::get_instance()->is_completed(),
335 + 'campaign_tour_seen' => 'yes' === get_user_meta( get_current_user_id(), Onboarding_API::TOUR_SEEN_META, true ),
336 + 'campaign_tour_progress' => $campaign_tour_progress,
337 + 'campaign_tour_pending' => $campaign_tour_pending,
195 338 'rotating_plugin_banner' => $this->get_rotating_plugin_banner(),
196 339 'smart_tags' => Helper::get_smart_tags(),
197 340 'is_pro_active' => defined( 'SUREDONATION_PRO_VER' ),
198 341 'pro_plugin_version' => defined( 'SUREDONATION_PRO_VER' ) ? SUREDONATION_PRO_VER : '',
@@ -205,17 +348,25 @@
205 348 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
206 349 'plugin_manager_nonce' => wp_create_nonce( 'suredonation_plugin_manager' ),
207 350 'has_abilities_api' => class_exists( 'WP_Ability' ),
208 351 'current_user_login' => wp_get_current_user()->user_login,
352 + 'integrations' => [
353 + 'sure_triggers' => Helper::get_ottokit_integration(),
354 + ],
209 355 // Bootstrap settings data so the General currency list and the
210 356 // PayPal connection state render synchronously (no post-mount
211 357 // fetch flicker). PayPal data is the non-sensitive subset only.
212 358 'payments' => [
213 - 'currencies' => Payment_Helper::get_currencies_list(),
214 - 'paypal' => PayPal_Helper::get_connection_state(),
359 + 'currencies' => Payment_Helper::get_currencies_list(),
360 + 'paypal' => PayPal_Helper::get_connection_state(),
361 + 'currency_sign_position' => Payment_Helper::get_currency_sign_position(),
362 + 'is_any_gateway_connected' => Payment_Helper::is_any_gateway_connected(),
363 + 'gateway_config_url' => Payment_Helper::get_settings_url( 'stripe' ),
215 364 ],
216 365 ]
217 366 );
367 +
368 + wp_localize_script( 'suredonation-admin', 'suredonation_admin', $localized_data );
218 369 }
219 370
220 371 /**
221 372 * Register admin menu.
@@ -464,28 +615,36 @@
464 615 } catch ( \Throwable $e ) {
465 616 $has_givewp_data = false;
466 617 }
467 618
619 + $has_charitable_data = false;
620 + try {
621 + $has_charitable_data = \SureDonation\Inc\Import\Charitable\Source::get_instance()->has_charitable_data();
622 + } catch ( \Throwable $e ) {
623 + $has_charitable_data = false;
624 + }
625 +
468 626 wp_localize_script(
469 627 'suredonation-onboarding',
470 628 'suredonation_onboarding',
471 629 [
472 - 'apiUrl' => rest_url( 'suredonation/v1' ),
473 - 'nonce' => wp_create_nonce( 'wp_rest' ),
474 - 'pluginUrl' => SUREDONATION_URL,
475 - 'adminUrl' => admin_url(),
476 - 'dashboardUrl' => admin_url( 'admin.php?page=suredonation' ),
477 - 'paymentsUrl' => admin_url( 'admin.php?page=suredonation#/settings?tab=payments' ),
478 - 'campaignsUrl' => admin_url( 'admin.php?page=suredonation#/campaigns' ),
479 - 'docsUrl' => 'https://suredonation.com/docs/',
480 - 'isProActive' => $is_pro,
481 - 'hasGiveWPData' => $has_givewp_data,
482 - 'currentUser' => [
630 + 'apiUrl' => rest_url( 'suredonation/v1' ),
631 + 'nonce' => wp_create_nonce( 'wp_rest' ),
632 + 'pluginUrl' => SUREDONATION_URL,
633 + 'adminUrl' => admin_url(),
634 + 'dashboardUrl' => admin_url( 'admin.php?page=suredonation' ),
635 + 'paymentsUrl' => Payment_Helper::get_settings_url( 'stripe' ),
636 + 'campaignsUrl' => admin_url( 'admin.php?page=suredonation#/campaigns' ),
637 + 'docsUrl' => 'https://suredonation.com/docs/',
638 + 'isProActive' => $is_pro,
639 + 'hasGiveWPData' => $has_givewp_data,
640 + 'hasCharitableData' => $has_charitable_data,
641 + 'currentUser' => [
483 642 'firstName' => $current_user ? $current_user->first_name : '',
484 643 'lastName' => $current_user ? $current_user->last_name : '',
485 644 'email' => $current_user ? $current_user->user_email : '',
486 645 ],
487 - 'privacyUrl' => 'https://suredonation.com/privacy-policy/',
646 + 'privacyUrl' => 'https://suredonation.com/privacy-policy/',
488 647 ]
489 648 );
490 649
491 650 // Shim suredonation_admin on the onboarding page too — shared API