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/blocks/register.php +44 -451 1.5.0 → 1.1.0 View file →
@@ -6,15 +6,11 @@
6 6 */
7 7
8 8 namespace SureDonation\Inc\Blocks;
9 9
10 -use SureDonation\Inc\Assets\Register as Assets_Register;
11 -use SureDonation\Inc\Helper;
12 10 use SureDonation\Inc\Payments\Offline\Offline_Helper;
13 -use SureDonation\Inc\Payments\PayPal\PayPal_Helper;
14 11 use SureDonation\Inc\Payments\Payment_Helper;
15 12 use SureDonation\Inc\Payments\Stripe\Stripe_Helper;
16 -use SureDonation\Inc\Post_Types\Donation_Form;
17 13 use SureDonation\Inc\Traits\Get_Instance;
18 14
19 15 // Exit if accessed directly.
20 16 if ( ! defined( 'ABSPATH' ) ) {
@@ -29,20 +25,8 @@
29 25 class Register {
30 26 use Get_Instance;
31 27
32 28 /**
33 - * Memoized block-inserter preview map (block key => image URL).
34 - *
35 - * Built once per request in get_field_preview_images() so every localized
36 - * copy is identical and the `suredonation_block_preview_images` filter runs
37 - * once.
38 - *
39 - * @var array<string,string>|null
40 - * @since 1.5.0
41 - */
42 - private $preview_images = null;
43 -
44 - /**
45 29 * Constructor.
46 30 *
47 31 * @since 0.0.1
48 32 */
@@ -50,128 +34,13 @@
50 34 add_action( 'init', [ $this, 'register_embed_block_script' ], 5 );
51 35 add_action( 'init', [ $this, 'register_blocks' ] );
52 36 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] );
53 37 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_campaign_editor_assets' ] );
54 - add_action( 'enqueue_block_editor_assets', [ $this, 'localize_embed_preview_images' ] );
55 38 add_filter( 'block_categories_all', [ $this, 'register_block_category' ], 10, 2 );
56 39 add_filter( 'block_editor_settings_all', [ $this, 'add_campaign_iframe_styles' ], 10, 2 );
57 - add_filter( 'block_editor_settings_all', [ $this, 'add_donation_form_iframe_styles' ], 10, 2 );
58 - add_filter( 'block_editor_settings_all', [ $this, 'add_phone_iframe_styles' ], 10, 2 );
59 - add_action( 'enqueue_block_assets', [ $this, 'enqueue_preview_field_scripts' ] );
60 40 }
61 41
62 42 /**
63 - * Load the dropdown and phone field libraries into the block editor canvas.
64 - *
65 - * The donation form embed block previews the real form through the block's PHP
66 - * render_callback (ServerSideRender), which returns markup only — a REST render
67 - * emits no wp_footer(), so nothing the render callback enqueues ever reaches the
68 - * page. Without their libraries the dropdown stays an unstyled native <select>
69 - * and the phone field renders with no country flag or dial code.
70 - *
71 - * `enqueue_block_assets` is the hook WordPress replays when it collects assets
72 - * for the iframed canvas: _wp_get_iframed_editor_assets() fires it and returns
73 - * both the printed styles AND scripts, which the canvas injects into its own
74 - * document. It is explicitly the hook for front-end assets that need to run
75 - * against editor content.
76 - *
77 - * Only the two field libraries are loaded. The payment gateways are excluded on
78 - * purpose: mounting Stripe Elements or the PayPal SDK would pull third-party
79 - * scripts into wp-admin on every editor load and open live gateway connections
80 - * for a preview the author cannot interact with. Those keep their static
81 - * placeholders (see _editor-preview.scss).
82 - *
83 - * Both initialisers are safe here — each is ready-state aware, guards against
84 - * double-initialising via a dataset flag, and exposes a re-init hook the editor
85 - * calls once ServerSideRender has injected the markup (see the block's edit
86 - * component).
87 - *
88 - * @return void
89 - * @since 1.4.0
90 - */
91 - public function enqueue_preview_field_scripts() {
92 - // Front end already enqueues these per-block from the field render; this is
93 - // the editor-only path.
94 - if ( ! is_admin() ) {
95 - return;
96 - }
97 -
98 - // The form builder mounts its own React controls for these fields.
99 - $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
100 - if ( $screen && 'suredonation_form' === $screen->post_type ) {
101 - return;
102 - }
103 -
104 - // The handles are registered on wp_enqueue_scripts, which never fires in
105 - // admin. Registration is side-effect free (wp_register_* only), so reuse it
106 - // rather than duplicating the definitions.
107 - Assets_Register::get_instance()->register_frontend_assets();
108 -
109 - // Each script handle already depends on its vendor library, so enqueuing the
110 - // initialiser pulls the library in, in the right order.
111 - wp_enqueue_style( 'suredonation-tom-select' );
112 - wp_enqueue_script( 'suredonation-dropdown' );
113 - wp_enqueue_style( 'suredonation-intl-tel-input' );
114 - wp_enqueue_script( 'suredonation-phone' );
115 -
116 - // The payment bundle mounts Stripe Elements and the PayPal buttons. Both are
117 - // client-only on mount: Stripe's elements()/mount() builds an iframe, and
118 - // PayPal's createOrder does not run until the button is clicked, so nothing
119 - // here reaches the server or creates a PaymentIntent.
120 - wp_enqueue_script( 'suredonation-form-frontend' );
121 -
122 - $this->enqueue_preview_gateway_assets();
123 - }
124 -
125 - /**
126 - * Enqueue gateway assets for each donation form embedded in the current post.
127 - *
128 - * The PayPal SDK is enqueued by gateway code hooked to
129 - * `suredonation_enqueue_form_frontend_scripts`, which the render callback fires
130 - * with the form's id and content — that hook is how a gateway decides whether it
131 - * is even used by the form. A REST render throws the enqueue away, so fire it
132 - * here instead, for the forms this post actually embeds.
133 - *
134 - * Resolving the forms (rather than loading every gateway unconditionally) keeps
135 - * the gateway's own `form_has_paypal()` style gating intact, so a post with no
136 - * PayPal-enabled form does not pull the SDK into wp-admin.
137 - *
138 - * Also localises the payment settings the bundle reads, per form.
139 - *
140 - * @return void
141 - * @since 1.4.0
142 - */
143 - private function enqueue_preview_gateway_assets() {
144 - $post = get_post();
145 -
146 - if ( ! $post instanceof \WP_Post || ! has_block( 'suredonation/donation-form', $post ) ) {
147 - return;
148 - }
149 -
150 - foreach ( parse_blocks( $post->post_content ) as $block ) {
151 - if ( 'suredonation/donation-form' !== ( $block['blockName'] ?? '' ) ) {
152 - continue;
153 - }
154 -
155 - $form_id = absint( $block['attrs']['formId'] ?? 0 );
156 - $form = $form_id ? get_post( $form_id ) : null;
157 -
158 - if ( ! $form instanceof \WP_Post || Donation_Form::POST_TYPE !== $form->post_type ) {
159 - continue;
160 - }
161 -
162 - /** This action is documented in inc/blocks/donation-form/block.php */
163 - do_action( 'suredonation_enqueue_form_frontend_scripts', $form_id, $form->post_content );
164 -
165 - wp_localize_script(
166 - 'suredonation-form-frontend',
167 - 'suredonationPayment',
168 - Helper::get_form_payment_settings( $form_id )
169 - );
170 - }
171 - }
172 -
173 - /**
174 43 * Register the donation form embed block editor script.
175 44 *
176 45 * Runs before register_blocks() so the handle exists when block.json is read.
177 46 * Not gated by post type — the embed block should work on all post types.
@@ -204,16 +73,8 @@
204 73 'suredonationCampaignBlocks',
205 74 $this->get_campaign_blocks_data()
206 75 );
207 76
208 - // Note: the inserter preview map is localized on this handle later, in
209 - // localize_embed_preview_images() on enqueue_block_editor_assets, not here
210 - // on init:5 — see that method for why.
211 -
212 - // Load JS translations for the embed editor bundle so the "Field preview"
213 - // string and the block's own UI strings can be translated.
214 - wp_set_script_translations( 'suredonation-donation-form-editor', 'suredonation' );
215 -
216 77 wp_register_style(
217 78 'suredonation-donation-form-editor',
218 79 SUREDONATION_URL . 'assets/build/blocks/donation-form/editor.css',
219 80 [],
@@ -221,34 +82,8 @@
221 82 );
222 83 }
223 84
224 85 /**
225 - * Localize the inserter preview map onto the donation-form embed script.
226 - *
227 - * The embed block is insertable on any post type, so this runs on every
228 - * editor screen (no post-type guard, unlike enqueue_editor_assets()).
229 - *
230 - * It runs on `enqueue_block_editor_assets` rather than at `init` — where the
231 - * handle is registered — deliberately: get_field_preview_images() memoizes on
232 - * its first call, so the first localizer to run fixes the map for the request.
233 - * Building it here (after `init`) lets consumers of the
234 - * `suredonation_block_preview_images` filter register on the default `init`
235 - * priority — the obvious thing to do, and what SureDonation Pro does on
236 - * `plugins_loaded` — and still be captured. The handle is registered by
237 - * register_embed_block_script() on init:5, so wp_localize_script attaches here.
238 - *
239 - * @return void
240 - * @since 1.5.0
241 - */
242 - public function localize_embed_preview_images() {
243 - wp_localize_script(
244 - 'suredonation-donation-form-editor',
245 - 'suredonation_fields_preview',
246 - $this->get_field_preview_images()
247 - );
248 - }
249 -
250 - /**
251 86 * Data localized for the block editor placeholders (logo).
252 87 *
253 88 * Shared by the donation form embed block and the campaign display blocks,
254 89 * both of which expose it on the `suredonationCampaignBlocks` JS global.
@@ -358,41 +193,45 @@
358 193 'suredonationCampaignBlocks',
359 194 $this->get_campaign_blocks_data()
360 195 );
361 196
362 - // Block-inserter preview images (see withFieldPreview / get_field_preview_images()).
363 - wp_localize_script(
197 + // Style the server-side-rendered block previews in the editor.
198 + $style_file = SUREDONATION_DIR . 'assets/build/blocks/campaign/style-style.css';
199 + $style_version = file_exists( $style_file )
200 + ? (string) filemtime( $style_file )
201 + : SUREDONATION_VER;
202 +
203 + wp_enqueue_style(
364 204 'suredonation-campaign-blocks',
365 - 'suredonation_fields_preview',
366 - $this->get_field_preview_images()
205 + SUREDONATION_URL . 'assets/build/blocks/campaign/style-style.css',
206 + [],
207 + $style_version
367 208 );
368 -
369 - // No stylesheet is enqueued here. The campaign blocks only ever render in
370 - // the canvas, and add_campaign_iframe_styles() inlines this exact CSS into
371 - // it through the editor settings, where it carries no element id and so is
372 - // invisible to the compatibility pass. Enqueueing it on the outer frame as
373 - // well put its one `.editor-styles-wrapper` rule in the admin document,
374 - // which is all it takes for WordPress to clone the whole sheet into the
375 - // canvas and log "suredonation-campaign-blocks-css was added to the iframe
376 - // incorrectly" on every campaign editor load.
377 209 }
378 210
379 211 /**
380 - * Append an inline stylesheet to the block-editor iframe settings.
212 + * Inject the campaign block styles into the editor canvas iframe.
381 213 *
382 214 * Styles enqueued via enqueue_block_editor_assets load in the editor's outer
383 - * frame only; the block canvas is iframed, so server-side-rendered previews
384 - * would otherwise render unstyled. Adding CSS here makes WordPress inject it
385 - * inside the iframe, matching the frontend.
215 + * frame only; the block canvas is iframed, so the server-side-rendered campaign
216 + * block previews would otherwise render unstyled. Adding the CSS to the editor
217 + * settings makes WordPress inject it inside the iframe, matching the frontend.
386 218 *
387 - * @param array<string, mixed> $settings Block editor settings (by reference).
388 - * @param string $css Stylesheet contents to inline.
389 - * @return void
390 - * @since 1.4.0
219 + * @param array<string, mixed> $settings Block editor settings.
220 + * @param \WP_Block_Editor_Context $context Block editor context.
221 + * @return array<string, mixed> Modified settings.
222 + * @since 1.0.0
391 223 */
392 - private function append_iframe_style( &$settings, $css ) {
224 + public function add_campaign_iframe_styles( $settings, $context ) {
225 + // Inject wherever the campaign blocks can be used (everywhere except the
226 + // donation form editor), so their editor previews match the frontend.
227 + if ( ! isset( $context->post ) || 'suredonation_form' === $context->post->post_type ) {
228 + return $settings;
229 + }
230 +
231 + $css = $this->get_campaign_iframe_css();
393 232 if ( '' === $css ) {
394 - return;
233 + return $settings;
395 234 }
396 235
397 236 if ( ! isset( $settings['styles'] ) || ! is_array( $settings['styles'] ) ) {
398 237 $settings['styles'] = [];
@@ -398,172 +237,41 @@
398 237 $settings['styles'] = [];
399 238 }
400 239
401 240 $settings['styles'][] = [ 'css' => $css ];
241 +
242 + return $settings;
402 243 }
403 244
404 245 /**
405 - * Read a stylesheet for iframe inlining, cached per request by file mtime so
406 - * the filter (which can run more than once per load) reads each file from disk
407 - * at most once until it changes.
246 + * Read the built campaign stylesheet, cached per request by file mtime so
247 + * the filter (which can run more than once per load) reads from disk at most
248 + * once until the asset changes.
408 249 *
409 - * @param string $style_file Absolute path to the stylesheet.
410 - * @param array<string, string> $replacements Optional search => replace pairs
411 - * applied to the CSS, e.g. to rewrite
412 - * relative asset URLs to absolute
413 - * plugin URLs so they resolve inside
414 - * the iframe.
415 250 * @return string The stylesheet contents, or '' when unavailable.
416 - * @since 1.4.0
251 + * @since 1.0.0
417 252 */
418 - private function read_iframe_css( $style_file, $replacements = [] ) {
419 - // Keyed by path so the aggregate + vendor stylesheets do not evict each
420 - // other's cache entry.
421 - static $cache = [];
253 + private function get_campaign_iframe_css() {
254 + static $cached_css = null;
255 + static $cached_mtime = null;
422 256
257 + $style_file = SUREDONATION_DIR . 'assets/build/blocks/campaign/style-style.css';
423 258 if ( ! file_exists( $style_file ) ) {
424 259 return '';
425 260 }
426 261
427 262 $mtime = filemtime( $style_file );
428 - if ( ! isset( $cache[ $style_file ] ) || $cache[ $style_file ]['mtime'] !== $mtime ) {
429 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading the plugin's own/vendored stylesheet to inline into the editor iframe.
430 - $css = file_get_contents( $style_file );
431 - $css = false === $css ? '' : $css;
432 -
433 - if ( '' !== $css && ! empty( $replacements ) ) {
434 - $css = str_replace( array_keys( $replacements ), array_values( $replacements ), $css );
435 - }
436 -
437 - $cache[ $style_file ] = [
438 - 'mtime' => $mtime,
439 - 'css' => $css,
440 - ];
263 + if ( null === $cached_css || $cached_mtime !== $mtime ) {
264 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading the plugin's own built stylesheet to inline into the editor iframe.
265 + $css = file_get_contents( $style_file );
266 + $cached_css = false === $css ? '' : $css;
267 + $cached_mtime = $mtime;
441 268 }
442 269
443 - return $cache[ $style_file ]['css'];
270 + return $cached_css;
444 271 }
445 272
446 273 /**
447 - * Inject the campaign block styles into the editor canvas iframe.
448 - *
449 - * @param array<string, mixed> $settings Block editor settings.
450 - * @param \WP_Block_Editor_Context $context Block editor context.
451 - * @return array<string, mixed> Modified settings.
452 - * @since 1.0.0
453 - */
454 - public function add_campaign_iframe_styles( $settings, $context ) {
455 - // Inject wherever the campaign blocks can be used, which is every editor
456 - // except the donation form builder — matching the blocks' own registration
457 - // and add_donation_form_iframe_styles() below.
458 - //
459 - // The post is checked only when there is one. Requiring it excluded exactly
460 - // the contexts that have none: the widget editor never sets it
461 - // (wp-admin/widgets-form-blocks.php), and the Site Editor only sets it for a
462 - // numeric postId, so editing any template or template part
463 - // (postId=theme//slug) has none either. The campaign blocks are insertable in
464 - // both, and this stylesheet also carries the placeholder rules the donation
465 - // form embed block reuses, so bailing there left both unstyled.
466 - if ( isset( $context->post ) && 'suredonation_form' === $context->post->post_type ) {
467 - return $settings;
468 - }
469 -
470 - $this->append_iframe_style(
471 - $settings,
472 - $this->read_iframe_css( SUREDONATION_DIR . 'assets/build/blocks/campaign/style-style.css' )
473 - );
474 -
475 - return $settings;
476 - }
477 -
478 - /**
479 - * Inject the donation form styles into the editor canvas iframe.
480 - *
481 - * The donation form embed block previews the real form via ServerSideRender,
482 - * and the canvas is iframed, so styles enqueued on the outer frame never reach
483 - * it. Three stylesheets are inlined:
484 - *
485 - * - the aggregate donation-form CSS, which also carries every field block's
486 - * styles and the editor-preview reconciliation (see _editor-preview.scss);
487 - * - the tom-select vendor CSS, which paints both the dropdown field's
488 - * server-rendered `.ts-wrapper` placeholder and the real control tom-select
489 - * mounts over it; and
490 - * - the intl-tel-input vendor CSS, for the `.iti` wrapper that library builds
491 - * around the phone input. Its flag sprites are referenced relative to the
492 - * stylesheet, so those paths are rewritten to absolute plugin URLs — inlining
493 - * drops the base they resolve against.
494 - *
495 - * Both libraries genuinely run in the canvas: they are enqueued on
496 - * enqueue_block_assets (see enqueue_preview_field_scripts) and re-initialised by
497 - * the block's edit component once ServerSideRender has injected the markup. The
498 - * payment gateways are not, so their placeholders stay static.
499 - *
500 - * @param array<string, mixed> $settings Block editor settings.
501 - * @param \WP_Block_Editor_Context $context Block editor context.
502 - * @return array<string, mixed> Modified settings.
503 - * @since 1.4.0
504 - */
505 - public function add_donation_form_iframe_styles( $settings, $context ) {
506 - // Inject wherever the embed block can be used, including the Site Editor and
507 - // widget contexts where $context->post is unset. Only the donation form
508 - // builder is excluded; it styles its own field blocks separately (see
509 - // add_phone_iframe_styles + form-editor).
510 - if ( isset( $context->post ) && 'suredonation_form' === $context->post->post_type ) {
511 - return $settings;
512 - }
513 -
514 - $this->append_iframe_style(
515 - $settings,
516 - $this->read_iframe_css( SUREDONATION_DIR . 'assets/build/blocks/donation-form/style-style.css' )
517 - );
518 - $this->append_iframe_style(
519 - $settings,
520 - $this->read_iframe_css( SUREDONATION_DIR . 'assets/css/vendor/tom-select.css' )
521 - );
522 - $this->append_iframe_style(
523 - $settings,
524 - $this->read_iframe_css(
525 - SUREDONATION_DIR . 'assets/css/vendor/intl/intlTelInput.min.css',
526 - [ '../intl/img/' => SUREDONATION_URL . 'assets/css/vendor/intl/img/' ]
527 - )
528 - );
529 -
530 - return $settings;
531 - }
532 -
533 - /**
534 - * Inject the intl-tel-input stylesheet into the editor canvas iframe.
535 - *
536 - * The phone block renders the real intl-tel-input control in the form builder
537 - * editor so its preview (flag + dial code) matches the front end. The canvas
538 - * is iframed, so the library CSS is added to the editor settings rather than
539 - * enqueued on the outer frame. Gated to the donation form editor, where the
540 - * phone block lives. (The relative flag sprite paths are rewritten to absolute
541 - * plugin URLs so they resolve inside the iframe.)
542 - *
543 - * @param array<string, mixed> $settings Block editor settings.
544 - * @param \WP_Block_Editor_Context $context Block editor context.
545 - * @return array<string, mixed> Modified settings.
546 - * @since 1.1.1
547 - */
548 - public function add_phone_iframe_styles( $settings, $context ) {
549 - // Only the donation form editor uses the field blocks (incl. phone).
550 - if ( ! isset( $context->post ) || 'suredonation_form' !== $context->post->post_type ) {
551 - return $settings;
552 - }
553 -
554 - $this->append_iframe_style(
555 - $settings,
556 - $this->read_iframe_css(
557 - SUREDONATION_DIR . 'assets/css/vendor/intl/intlTelInput.min.css',
558 - [ '../intl/img/' => SUREDONATION_URL . 'assets/css/vendor/intl/img/' ]
559 - )
560 - );
561 -
562 - return $settings;
563 - }
564 -
565 - /**
566 274 * Enqueue block editor assets.
567 275 *
568 276 * Only loads on the donation form editor.
569 277 *
@@ -609,13 +317,10 @@
609 317 'suredonation_admin',
610 318 [
611 319 'payments' => [
612 320 'stripe_connected' => Stripe_Helper::is_stripe_connected(),
613 - 'paypal_connected' => PayPal_Helper::is_paypal_connected(),
614 321 'stripe_connect_url' => Stripe_Helper::get_stripe_connect_url(),
615 - // Base payments-settings URL; the editor's "Configure Payment
616 - // Account" CTA appends the block's selected gateway subpage.
617 - 'settings_url' => Payment_Helper::get_settings_url(),
322 + 'settings_url' => admin_url( 'admin.php?page=suredonation#/settings?tab=payments' ),
618 323 'offline_enabled' => Offline_Helper::is_offline_enabled(),
619 324 'gateways' => apply_filters(
620 325 'suredonation_editor_payment_gateways',
621 326 [
@@ -639,120 +344,8 @@
639 344 // them as placeholders on each field's Error Message control.
640 345 'validationMessages' => \SureDonation\Inc\Field_Validation::get_resolved_validation_messages(),
641 346 ]
642 347 );
643 -
644 - // Field-preview images shown in the block inserter's preview pane (mirrors
645 - // SureForms). Blocks whose block.json sets example.attributes.preview render
646 - // this image via the withFieldPreview HOC; unmapped blocks fall back to the
647 - // shared placeholder key on the JS side.
648 - wp_localize_script(
649 - 'suredonation-blocks',
650 - 'suredonation_fields_preview',
651 - $this->get_field_preview_images()
652 - );
653 - }
654 -
655 - /**
656 - * Block-inserter preview images, keyed by block name (slug, hyphens as
657 - * underscores) to mirror the JS lookup in withFieldPreview.
658 - *
659 - * Shared by every editor bundle (field blocks, campaign blocks, donation-form
660 - * embed) so the same map is localized wherever a SureDonation block can be
661 - * inserted. Field-type blocks reuse the SureForms field-preview art; campaign
662 - * and donation-form display blocks use SureDonation's own mockups; anything not
663 - * listed falls back to the shared placeholder on the JS side.
664 - *
665 - * Pro-only field blocks (date/time picker) register their own art through the
666 - * `suredonation_block_preview_images` filter — Pro owns those assets, so they
667 - * are not listed here.
668 - *
669 - * Memoized so the map is built once per request: every localized copy is then
670 - * identical regardless of which editor hook fires first, and the filter runs
671 - * exactly once.
672 - *
673 - * @return array<string,string> Map of block key => image URL.
674 - * @since 1.5.0
675 - */
676 - public function get_field_preview_images() {
677 - if ( null !== $this->preview_images ) {
678 - return $this->preview_images;
679 - }
680 -
681 - $base = SUREDONATION_URL . 'images/field-previews/';
682 -
683 - /**
684 - * Filters the block-inserter preview image map.
685 - *
686 - * Keys are block slugs with hyphens replaced by underscores
687 - * (`suredonation/date-picker` => `date_picker`) to match the JS lookup in
688 - * withFieldPreview; values are absolute image URLs.
689 - *
690 - * Register no later than `init` — SureDonation Pro adds its date/time
691 - * picker art on `plugins_loaded`. The map is first built on
692 - * `enqueue_block_editor_assets` and then memoized, so a filter added after
693 - * that first build is silently ignored.
694 - *
695 - * @since 1.5.0
696 - *
697 - * @param array<string,string> $images Map of block key => image URL.
698 - */
699 - $images = apply_filters(
700 - 'suredonation_block_preview_images',
701 - [
702 - // Field blocks.
703 - 'input' => $base . 'input.svg',
704 - 'email' => $base . 'email.svg',
705 - 'number' => $base . 'number.svg',
706 - 'checkbox' => $base . 'checkbox.svg',
707 - // Anonymous-donation and cover-fees both render a single checkbox.
708 - 'anonymous_donation' => $base . 'checkbox.svg',
709 - 'cover_fees' => $base . 'checkbox.svg',
710 - 'dropdown' => $base . 'dropdown.svg',
711 - // Donation-amount is a grid of selectable amounts (radio options).
712 - 'donation_amount' => $base . 'multi-choice.svg',
713 - 'address' => $base . 'address.svg',
714 - 'phone' => $base . 'phone.svg',
715 - 'url' => $base . 'url.svg',
716 - 'heading' => $base . 'heading.svg',
717 - 'html' => $base . 'html.svg',
718 - 'image' => $base . 'image.svg',
719 - 'payment' => $base . 'payment.svg',
720 - 'donate_button' => $base . 'button.svg',
721 - // Campaign + donation-form display blocks.
722 - 'donation_form' => $base . 'donation-form.svg',
723 - 'campaign_goal' => $base . 'campaign-goal.svg',
724 - 'campaign_stats' => $base . 'campaign-stats.svg',
725 - 'campaign_donations' => $base . 'campaign-donations.svg',
726 - 'campaign_donors' => $base . 'campaign-donors.svg',
727 - 'campaign_social_sharing' => $base . 'campaign-social-sharing.svg',
728 - 'campaign_donate_button' => $base . 'button.svg',
729 - // Fallback for any block without its own image.
730 - 'placeholder' => $base . 'placeholder.svg',
731 - ]
732 - );
733 -
734 - // Coerce defensively: a filter returning a non-array or non-string values
735 - // must not poison the map (mirrors the JS-side guard).
736 - if ( ! is_array( $images ) ) {
737 - $this->preview_images = [];
738 - return $this->preview_images;
739 - }
740 -
741 - $result = [];
742 - foreach ( $images as $key => $value ) {
743 - if ( is_string( $key ) && is_string( $value ) ) {
744 - // esc_url_raw for consistency with get_campaign_blocks_data(); the
745 - // value is consumed by JS as an <img src>, not printed as HTML. The
746 - // plugin version busts the browser cache when redesigned art ships
747 - // in a release (filemtime is avoided: filter-supplied Pro URLs do
748 - // not resolve to a local path).
749 - $result[ $key ] = esc_url_raw( add_query_arg( 'ver', SUREDONATION_VER, $value ) );
750 - }
751 - }
752 -
753 - $this->preview_images = $result;
754 - return $this->preview_images;
755 348 }
756 349
757 350 /**
758 351 * Register all blocks.