PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.1.0
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.1.0
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/form-editor/assets.php +43 -179 1.5.0 → 1.1.0 View file →
@@ -34,11 +34,8 @@
34 34 * @since 0.0.1
35 35 */
36 36 public function __construct() {
37 37 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] );
38 - // The stylesheet is registered separately, on the hook WordPress replays
39 - // inside the editor canvas iframe. See enqueue_editor_styles().
40 - add_action( 'enqueue_block_assets', [ $this, 'enqueue_editor_styles' ] );
41 38 add_action( 'init', [ $this, 'register_form_settings_meta' ] );
42 39 add_action( 'init', [ $this, 'register_email_notifications_meta' ] );
43 40 }
44 41
@@ -48,22 +45,31 @@
48 45 * @return void
49 46 * @since 0.0.1
50 47 */
51 48 public function enqueue_editor_assets() {
49 + $screen = get_current_screen();
50 +
52 51 // Only load on donation form editor.
53 - if ( ! $this->is_form_editor_screen() ) {
52 + if ( ! $screen || Donation_Form::POST_TYPE !== $screen->post_type ) {
54 53 return;
55 54 }
56 55
57 - // Core's bundled CodeMirror (CSS mode) backs the Custom CSS tab in the form
58 - // settings dialog. Returns false when the user turned syntax highlighting
59 - // off in their profile; the tab falls back to a plain textarea then.
60 - wp_enqueue_code_editor( [ 'type' => 'text/css' ] );
61 - wp_enqueue_script( 'wp-theme-plugin-editor' );
62 - wp_enqueue_style( 'wp-codemirror' );
56 + // Get the asset file for dependencies.
57 + $editor_asset_file = SUREDONATION_DIR . 'assets/build/editor/editor.asset.php';
58 + $editor_asset = file_exists( $editor_asset_file )
59 + ? include $editor_asset_file
60 + : [
61 + 'dependencies' => [
62 + 'wp-plugins',
63 + 'wp-editor',
64 + 'wp-components',
65 + 'wp-data',
66 + 'wp-element',
67 + 'wp-i18n',
68 + ],
69 + 'version' => SUREDONATION_VER,
70 + ];
63 71
64 - $editor_asset = $this->get_editor_asset();
65 -
66 72 // Editor plugin JS.
67 73 wp_enqueue_script(
68 74 'suredonation-form-editor',
69 75 SUREDONATION_URL . 'assets/build/editor/editor.js',
@@ -71,55 +77,33 @@
71 77 $editor_asset['version'],
72 78 true
73 79 );
74 80
75 - // Editor styles are enqueued from enqueue_editor_styles(), not here.
81 + // Editor styles.
82 + wp_enqueue_style(
83 + 'suredonation-form-editor',
84 + SUREDONATION_URL . 'assets/build/editor/editor.css',
85 + [ 'wp-components' ],
86 + $editor_asset['version']
87 + );
76 88
77 - // The OttoKit embed script (defines window.SureTriggers) is NOT enqueued
78 - // here — it is remote executable JS and would load on every form-editor
79 - // session even when OttoKit is absent. It is lazy-injected from the
80 - // OttoKit tab only when the builder opens (see OttoKitSettings.js), using
81 - // the URL passed below.
82 -
83 89 // Localized data.
84 90 $global_currency = Payment_Helper::get_global_setting( 'currency', 'USD' );
85 91 $global_currency = is_string( $global_currency ) ? $global_currency : 'USD';
86 92
87 - $editor_data = [
88 - 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
89 - 'nonce' => wp_create_nonce( 'suredonation_editor_nonce' ),
90 - 'postType' => Donation_Form::POST_TYPE,
91 - 'smartTags' => Helper::get_smart_tags()['confirmation'],
92 - 'emailSmartTags' => Helper::get_smart_tags()['email_grouped'],
93 - 'defaultEmailNotifications' => $this->get_default_email_notifications(),
94 - // The recurring payment type is a Pro control, but the block attribute
95 - // it sets is registered in free and persists in post content. Without
96 - // this, deactivating Pro leaves the recurring notifications on screen
97 - // and editable while nothing can send them.
98 - 'isProActive' => defined( 'SUREDONATION_PRO_VER' ),
99 - 'currency' => $global_currency,
100 - 'currencySymbol' => Payment_Helper::get_currency_symbol( $global_currency ),
101 - // OttoKit integration: embed config + lazy-loaded builder script.
102 - 'suretriggersNonce' => wp_create_nonce( 'suredonation_suretriggers_nonce' ),
103 - 'embedScriptUrl' => SUREDONATION_SURETRIGGERS_INTEGRATION_BASE_URL . 'js/v2/embed.js',
104 - 'integrations' => [
105 - 'sure_triggers' => Helper::get_ottokit_integration(),
106 - ],
107 - ];
108 -
109 - // Plugin install/activate nonces are a plugin-management capability;
110 - // only expose them to users who can actually install plugins. The AJAX
111 - // handlers enforce this server-side too — this keeps the nonces out of
112 - // the page source for sub-admins who reach the editor via post.php.
113 - if ( current_user_can( 'install_plugins' ) ) {
114 - $editor_data['pluginInstallerNonce'] = wp_create_nonce( 'updates' );
115 - $editor_data['pluginManagerNonce'] = wp_create_nonce( 'suredonation_plugin_manager' );
116 - }
117 -
118 93 wp_localize_script(
119 94 'suredonation-form-editor',
120 95 'suredonationFormEditor',
121 - $editor_data
96 + [
97 + 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
98 + 'nonce' => wp_create_nonce( 'suredonation_editor_nonce' ),
99 + 'postType' => Donation_Form::POST_TYPE,
100 + 'smartTags' => Helper::get_smart_tags()['confirmation'],
101 + 'emailSmartTags' => Helper::get_smart_tags()['email_grouped'],
102 + 'defaultEmailNotifications' => $this->get_default_email_notifications(),
103 + 'currency' => $global_currency,
104 + 'currencySymbol' => Payment_Helper::get_currency_symbol( $global_currency ),
105 + ]
122 106 );
123 107
124 108 // Set script translations.
125 109 wp_set_script_translations( 'suredonation-form-editor', 'suredonation' );
@@ -125,102 +109,8 @@
125 109 wp_set_script_translations( 'suredonation-form-editor', 'suredonation' );
126 110 }
127 111
128 112 /**
129 - * Enqueue the form editor stylesheet.
130 - *
131 - * Split out from enqueue_editor_assets() because the two need different
132 - * hooks. `enqueue_block_editor_assets` only reaches the admin document, but
133 - * most of this stylesheet targets `.editor-styles-wrapper` — the block
134 - * canvas, which is an iframe. WordPress used to paper over that with a
135 - * compatibility pass that clones any outer stylesheet mentioning
136 - * `.editor-styles-wrapper` into the canvas, logging "<handle> was added to
137 - * the iframe incorrectly" for each one (see the block editor's `Iframe`
138 - * component). `enqueue_block_assets` is the supported hook instead:
139 - * core replays it inside _wp_get_iframed_editor_assets() to build the
140 - * canvas document, so the styles land there directly and the compatibility
141 - * pass skips the handle instead of cloning it.
142 - *
143 - * The hook fires for the admin document as well, so a single enqueue here
144 - * covers the editor chrome too and the handle stays `suredonation-form-editor`
145 - * — the id core matches on when deciding whether a clone is still needed.
146 - *
147 - * One consequence of the move: `enqueue_block_assets` reaches the admin
148 - * document only through wp_common_block_scripts_and_styles(), which bails when
149 - * `should_load_block_editor_scripts_and_styles` is filtered false in wp-admin.
150 - * Anything doing that also strips `wp-block-library` and visibly breaks core's
151 - * own editor, so it is not a case worth defending against here.
152 - *
153 - * @return void
154 - * @since x.x.x
155 - */
156 - public function enqueue_editor_styles() {
157 - // `enqueue_block_assets` also fires on the front end, where there is no
158 - // editor to style.
159 - if ( ! is_admin() || ! $this->is_form_editor_screen() ) {
160 - return;
161 - }
162 -
163 - /*
164 - * No `wp-components` dependency. It is already in both documents ahead of
165 - * this sheet without being asked for: the admin page loads it as editor
166 - * chrome, and _wp_get_iframed_editor_assets() enqueues `wp-edit-blocks`
167 - * — whose dependencies include `wp-components` — before it fires
168 - * `enqueue_block_assets`. Declaring it would neither change the cascade nor
169 - * keep anything out of the canvas.
170 - */
171 - wp_enqueue_style(
172 - 'suredonation-form-editor',
173 - SUREDONATION_URL . 'assets/build/editor/editor.css',
174 - [],
175 - $this->get_editor_asset()['version']
176 - );
177 - }
178 -
179 - /**
180 - * Whether the current admin screen is the donation form editor.
181 - *
182 - * @return bool
183 - * @since x.x.x
184 - */
185 - private function is_form_editor_screen() {
186 - // get_current_screen() lives in an admin include, so it is missing on the
187 - // front end — where enqueue_block_assets also fires. Checked here rather
188 - // than at each call site so the helper is safe for any caller.
189 - if ( ! function_exists( 'get_current_screen' ) ) {
190 - return false;
191 - }
192 -
193 - $screen = get_current_screen();
194 -
195 - return $screen instanceof \WP_Screen && Donation_Form::POST_TYPE === $screen->post_type;
196 - }
197 -
198 - /**
199 - * Build metadata (dependencies and version) for the editor bundle.
200 - *
201 - * @return array{dependencies: array<int, string>, version: string}
202 - * @since x.x.x
203 - */
204 - private function get_editor_asset() {
205 - $editor_asset_file = SUREDONATION_DIR . 'assets/build/editor/editor.asset.php';
206 -
207 - return file_exists( $editor_asset_file )
208 - ? include $editor_asset_file
209 - : [
210 - 'dependencies' => [
211 - 'wp-plugins',
212 - 'wp-editor',
213 - 'wp-components',
214 - 'wp-data',
215 - 'wp-element',
216 - 'wp-i18n',
217 - ],
218 - 'version' => SUREDONATION_VER,
219 - ];
220 - }
221 -
222 - /**
223 113 * Register form settings meta fields.
224 114 *
225 115 * Stores form confirmation settings as a single JSON string.
226 116 *
@@ -293,9 +183,8 @@
293 183 // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found -- Readability.
294 184 $defaults = [
295 185 // --- Donor Emails ---
296 186 [
297 - 'key' => 'donation_receipt',
298 187 'id' => 1,
299 188 'status' => true,
300 189 'name' => __( 'Donation Receipt', 'suredonation' ),
301 190 'email_to' => '{donor_email}',
@@ -314,9 +203,8 @@
314 203 'reply_to' => '',
315 204 'trigger' => 'donation_completed',
316 205 ],
317 206 [
318 - 'key' => 'donation_processing',
319 207 'id' => 2,
320 208 'status' => true,
321 209 'name' => __( 'Donation Processing', 'suredonation' ),
322 210 'email_to' => '{donor_email}',
@@ -334,9 +222,8 @@
334 222 'reply_to' => '',
335 223 'trigger' => 'donation_processing',
336 224 ],
337 225 [
338 - 'key' => 'donation_failed',
339 226 'id' => 3,
340 227 'status' => true,
341 228 'name' => __( 'Donation Failed', 'suredonation' ),
342 229 'email_to' => '{donor_email}',
@@ -350,9 +237,8 @@
350 237 'reply_to' => '',
351 238 'trigger' => 'donation_failed',
352 239 ],
353 240 [
354 - 'key' => 'refund_processed',
355 241 'id' => 4,
356 242 'status' => true,
357 243 'name' => __( 'Refund Processed', 'suredonation' ),
358 244 'email_to' => '{donor_email}',
@@ -372,9 +258,8 @@
372 258 ],
373 259
374 260 // --- Admin Emails ---
375 261 [
376 - 'key' => 'donation_receipt_admin',
377 262 'id' => 9,
378 263 'status' => true,
379 264 'name' => __( 'New Donation (Admin)', 'suredonation' ),
380 265 'email_to' => '{admin_email}',
@@ -393,9 +278,8 @@
393 278 'reply_to' => '',
394 279 'trigger' => 'donation_completed',
395 280 ],
396 281 [
397 - 'key' => 'donation_failed_admin',
398 282 'id' => 10,
399 283 'status' => true,
400 284 'name' => __( 'Donation Failed (Admin)', 'suredonation' ),
401 285 'email_to' => '{admin_email}',
@@ -413,9 +297,8 @@
413 297 'reply_to' => '',
414 298 'trigger' => 'donation_failed',
415 299 ],
416 300 [
417 - 'key' => 'refund_processed_admin',
418 301 'id' => 11,
419 302 'status' => true,
420 303 'name' => __( 'Refund Processed (Admin)', 'suredonation' ),
421 304 'email_to' => '{admin_email}',
@@ -499,30 +382,9 @@
499 382 if ( ! is_array( $notification ) ) {
500 383 continue;
501 384 }
502 385
503 - $trigger = isset( $notification['trigger'] ) && is_string( $notification['trigger'] ) ? $notification['trigger'] : '';
504 -
505 - // Preserve the trigger verbatim rather than validating against the
506 - // registered list. 'all' means "send on every event", so coercing an
507 - // unrecognised trigger to it silently rewires that notification to
508 - // fire on every donation event. Saving a form while Pro is inactive
509 - // did exactly that to the recurring templates: the trigger was not
510 - // registered, so a customised "Subscription Created" became a message
511 - // sent on completed, failed, processing and refunded donations, and
512 - // the editor stopped recognising it as recurring and appended a
513 - // duplicate set.
514 - //
515 - // An unregistered trigger is already inert: dispatch is an equality
516 - // match against an event name, and the code that fires the recurring
517 - // events does not load while Pro is inactive. Preserving the value
518 - // costs nothing and lets the notification resume working, with its
519 - // customisations, as soon as Pro is active again.
520 386 $sanitized[] = [
521 - // Stable machine identity. `id` is reassigned when a set is
522 - // re-seeded and `name` is user-editable and translated, so neither
523 - // survives as a way to recognise a notification later.
524 - 'key' => isset( $notification['key'] ) && is_string( $notification['key'] ) ? sanitize_key( $notification['key'] ) : '',
525 387 'id' => isset( $notification['id'] ) ? absint( $notification['id'] ) : 0,
526 388 'status' => isset( $notification['status'] ) ? (bool) $notification['status'] : true,
527 389 'name' => isset( $notification['name'] ) ? sanitize_text_field( $notification['name'] ) : '',
528 390 'email_to' => isset( $notification['email_to'] ) ? sanitize_text_field( $notification['email_to'] ) : '',
@@ -530,13 +392,16 @@
530 392 'email_body' => isset( $notification['email_body'] ) ? wp_kses_post( $notification['email_body'] ) : '',
531 393 'from_name' => isset( $notification['from_name'] ) ? sanitize_text_field( $notification['from_name'] ) : '',
532 394 'from_email' => isset( $notification['from_email'] ) ? sanitize_text_field( $notification['from_email'] ) : '',
533 395 'reply_to' => isset( $notification['reply_to'] ) ? sanitize_text_field( $notification['reply_to'] ) : '',
534 - // A missing trigger must stay inert. Dispatch skips an empty
535 - // trigger but treats 'all' as "fire on every event", so falling
536 - // back to 'all' routes the unknown case to the most permissive
537 - // outcome — the opposite of what a missing value should mean.
538 - 'trigger' => '' !== $trigger ? sanitize_key( $trigger ) : '',
396 + 'trigger' => isset( $notification['trigger'] ) && in_array(
397 + $notification['trigger'],
398 + apply_filters(
399 + 'suredonation_email_notification_triggers',
400 + [ 'all', 'donation_completed', 'donation_processing', 'donation_failed', 'refund_processed' ]
401 + ),
402 + true
403 + ) ? $notification['trigger'] : 'all',
539 404 ];
540 405 }
541 406
542 407 $encoded = wp_json_encode( $sanitized );
@@ -541,9 +406,8 @@
541 406
542 407 $encoded = wp_json_encode( $sanitized );
543 408 return is_string( $encoded ) ? $encoded : '';
544 409 }
545 -
546 410
547 411 /**
548 412 * Sanitize form confirmation settings.
549 413 *