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
suredonation / inc / blocks / register.php

register.php in SureDonation – Donation Forms, Fundraising Campaigns & Donor Management 1.1.0, at inc/blocks/register.php

429 lines 13.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Blocks Register
4 *
5 * @package SureDonation
6 */
7
8 namespace SureDonation\Inc\Blocks;
9
10 use SureDonation\Inc\Payments\Offline\Offline_Helper;
11 use SureDonation\Inc\Payments\Payment_Helper;
12 use SureDonation\Inc\Payments\Stripe\Stripe_Helper;
13 use SureDonation\Inc\Traits\Get_Instance;
14
15 // Exit if accessed directly.
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 /**
21 * Register class for blocks.
22 *
23 * @since 0.0.1
24 */
25 class Register {
26 use Get_Instance;
27
28 /**
29 * Constructor.
30 *
31 * @since 0.0.1
32 */
33 public function __construct() {
34 add_action( 'init', [ $this, 'register_embed_block_script' ], 5 );
35 add_action( 'init', [ $this, 'register_blocks' ] );
36 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] );
37 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_campaign_editor_assets' ] );
38 add_filter( 'block_categories_all', [ $this, 'register_block_category' ], 10, 2 );
39 add_filter( 'block_editor_settings_all', [ $this, 'add_campaign_iframe_styles' ], 10, 2 );
40 }
41
42 /**
43 * Register the donation form embed block editor script.
44 *
45 * Runs before register_blocks() so the handle exists when block.json is read.
46 * Not gated by post type — the embed block should work on all post types.
47 *
48 * @return void
49 * @since 1.0.0
50 */
51 public function register_embed_block_script() {
52 $asset_file = SUREDONATION_DIR . 'assets/build/blocks/donation-form/editor.asset.php';
53 $asset = file_exists( $asset_file )
54 ? require $asset_file
55 : [
56 'dependencies' => [],
57 'version' => SUREDONATION_VER,
58 ];
59
60 wp_register_script(
61 'suredonation-donation-form-editor',
62 SUREDONATION_URL . 'assets/build/blocks/donation-form/editor.js',
63 $asset['dependencies'],
64 $asset['version'],
65 true
66 );
67
68 // Data for the block editor placeholder (logo). The campaign blocks
69 // bundle defines the same global elsewhere; localizing it here keeps the
70 // logo available wherever the donation form block is inserted.
71 wp_localize_script(
72 'suredonation-donation-form-editor',
73 'suredonationCampaignBlocks',
74 $this->get_campaign_blocks_data()
75 );
76
77 wp_register_style(
78 'suredonation-donation-form-editor',
79 SUREDONATION_URL . 'assets/build/blocks/donation-form/editor.css',
80 [],
81 $asset['version']
82 );
83 }
84
85 /**
86 * Data localized for the block editor placeholders (logo).
87 *
88 * Shared by the donation form embed block and the campaign display blocks,
89 * both of which expose it on the `suredonationCampaignBlocks` JS global.
90 *
91 * `currentPostType` lets a block scope its editor registration to a single
92 * post type (the Campaign Donate Button registers only on the campaign
93 * editor). It is read from the current screen, so it is only populated for
94 * the caller that runs on `enqueue_block_editor_assets` (the campaign editor
95 * assets); the embed-block caller runs on `init`, where there is no screen,
96 * so it receives an empty string. That is harmless — the embed block only
97 * consumes `logoUrl`.
98 *
99 * @return array<string, string>
100 * @since 1.0.0
101 */
102 public function get_campaign_blocks_data() {
103 $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
104
105 return [
106 'logoUrl' => esc_url_raw( SUREDONATION_URL . 'images/suredonation-logo.svg' ),
107 'currentPostType' => $screen ? (string) $screen->post_type : '',
108 ];
109 }
110
111 /**
112 * Register custom block category for SureDonation blocks.
113 *
114 * The field-block category is limited to the donation form editor; the
115 * campaign display-block category is registered everywhere else.
116 *
117 * @param array<int, array<string, mixed>> $categories Existing block categories.
118 * @param \WP_Block_Editor_Context $context Block editor context.
119 * @return array<int, array<string, mixed>> Modified block categories.
120 * @since 0.0.1
121 */
122 public function register_block_category( $categories, $context ) {
123 // Field-block category on the donation form editor.
124 if ( isset( $context->post ) && 'suredonation_form' === $context->post->post_type ) {
125 return array_merge(
126 [
127 [
128 'slug' => 'suredonation',
129 'title' => __( 'General Fields', 'suredonation' ),
130 'icon' => null,
131 ],
132 ],
133 $categories
134 );
135 }
136
137 // Campaign display-block category on every other editor — including the
138 // Site Editor and widget contexts where $context->post is unset — so the
139 // campaign blocks always group under SureDonation in the inserter. Only
140 // the donation form editor (handled above) is excluded.
141 return array_merge(
142 [
143 [
144 'slug' => 'suredonation-campaign',
145 'title' => __( 'SureDonation', 'suredonation' ),
146 'icon' => null,
147 ],
148 ],
149 $categories
150 );
151 }
152
153 /**
154 * Enqueue the campaign display blocks editor bundle.
155 *
156 * Loads on every block editor so the campaign blocks can be added to any
157 * page/post/CPT — except the donation form editor, which has its own field
158 * blocks. On a campaign post the blocks auto-bind to that campaign; elsewhere
159 * the block inspector exposes a campaign selector.
160 *
161 * @return void
162 * @since 1.0.0
163 */
164 public function enqueue_campaign_editor_assets() {
165 $screen = get_current_screen();
166
167 // Load everywhere except the donation form editor.
168 if ( ! $screen || 'suredonation_form' === $screen->post_type ) {
169 return;
170 }
171
172 $asset_file = SUREDONATION_DIR . 'assets/build/campaign-blocks.asset.php';
173 $asset = file_exists( $asset_file )
174 ? require $asset_file
175 : [
176 'dependencies' => [ 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n', 'wp-block-editor', 'wp-data', 'wp-server-side-render' ],
177 'version' => SUREDONATION_VER,
178 ];
179
180 wp_enqueue_script(
181 'suredonation-campaign-blocks',
182 SUREDONATION_URL . 'assets/build/campaign-blocks.js',
183 $asset['dependencies'],
184 $asset['version'],
185 true
186 );
187
188 wp_set_script_translations( 'suredonation-campaign-blocks', 'suredonation' );
189
190 // Data for the campaign block editor placeholder (logo).
191 wp_localize_script(
192 'suredonation-campaign-blocks',
193 'suredonationCampaignBlocks',
194 $this->get_campaign_blocks_data()
195 );
196
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(
204 'suredonation-campaign-blocks',
205 SUREDONATION_URL . 'assets/build/blocks/campaign/style-style.css',
206 [],
207 $style_version
208 );
209 }
210
211 /**
212 * Inject the campaign block styles into the editor canvas iframe.
213 *
214 * Styles enqueued via enqueue_block_editor_assets load in the editor's outer
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.
218 *
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
223 */
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();
232 if ( '' === $css ) {
233 return $settings;
234 }
235
236 if ( ! isset( $settings['styles'] ) || ! is_array( $settings['styles'] ) ) {
237 $settings['styles'] = [];
238 }
239
240 $settings['styles'][] = [ 'css' => $css ];
241
242 return $settings;
243 }
244
245 /**
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.
249 *
250 * @return string The stylesheet contents, or '' when unavailable.
251 * @since 1.0.0
252 */
253 private function get_campaign_iframe_css() {
254 static $cached_css = null;
255 static $cached_mtime = null;
256
257 $style_file = SUREDONATION_DIR . 'assets/build/blocks/campaign/style-style.css';
258 if ( ! file_exists( $style_file ) ) {
259 return '';
260 }
261
262 $mtime = filemtime( $style_file );
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;
268 }
269
270 return $cached_css;
271 }
272
273 /**
274 * Enqueue block editor assets.
275 *
276 * Only loads on the donation form editor.
277 *
278 * @return void
279 * @since 0.0.1
280 */
281 public function enqueue_editor_assets() {
282 $screen = get_current_screen();
283
284 // Only load on donation form editor.
285 if ( ! $screen || 'suredonation_form' !== $screen->post_type ) {
286 return;
287 }
288
289 // Use the asset.php content hash as the version so rebuilds bust the
290 // browser cache. Falls back to SUREDONATION_VER if the asset file
291 // is missing.
292 $blocks_asset_file = SUREDONATION_DIR . 'assets/build/blocks.asset.php';
293 $blocks_asset = file_exists( $blocks_asset_file )
294 ? require $blocks_asset_file
295 : [
296 'dependencies' => [ 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n', 'wp-block-editor', 'wp-data' ],
297 'version' => SUREDONATION_VER,
298 ];
299
300 // Enqueue the blocks script.
301 wp_enqueue_script(
302 'suredonation-blocks',
303 SUREDONATION_URL . 'assets/build/blocks.js',
304 $blocks_asset['dependencies'],
305 $blocks_asset['version'],
306 true
307 );
308
309 // Load JS translations for blocks.
310 wp_set_script_translations( 'suredonation-blocks', 'suredonation' );
311
312 // Localize script with admin data for blocks.
313 $global_currency = Payment_Helper::get_currency();
314
315 wp_localize_script(
316 'suredonation-blocks',
317 'suredonation_admin',
318 [
319 'payments' => [
320 'stripe_connected' => Stripe_Helper::is_stripe_connected(),
321 'stripe_connect_url' => Stripe_Helper::get_stripe_connect_url(),
322 'settings_url' => admin_url( 'admin.php?page=suredonation#/settings?tab=payments' ),
323 'offline_enabled' => Offline_Helper::is_offline_enabled(),
324 'gateways' => apply_filters(
325 'suredonation_editor_payment_gateways',
326 [
327 [
328 'value' => 'stripe',
329 'label' => __( 'Stripe', 'suredonation' ),
330 'supports_recurring' => true,
331 ],
332 [
333 'value' => 'offline',
334 'label' => __( 'Offline Donations', 'suredonation' ),
335 'supports_recurring' => false,
336 ],
337 ]
338 ),
339 ],
340 'fee_recovery' => Payment_Helper::get_fee_recovery_settings(),
341 'currency' => $global_currency,
342 'currencySymbol' => Payment_Helper::get_currency_symbol( $global_currency ),
343 // Resolved default validation messages so the editor can show
344 // them as placeholders on each field's Error Message control.
345 'validationMessages' => \SureDonation\Inc\Field_Validation::get_resolved_validation_messages(),
346 ]
347 );
348 }
349
350 /**
351 * Register all blocks.
352 *
353 * @return void
354 * @since 0.0.1
355 */
356 public function register_blocks() {
357 $blocks = [
358 [
359 'dir' => SUREDONATION_DIR . 'inc/blocks/**/*.php',
360 'namespace' => 'SureDonation\\Inc\\Blocks',
361 ],
362 ];
363
364 /**
365 * Filter to add and register additional blocks.
366 *
367 * @param array<int, array<string, string>> $additional_blocks Additional blocks to register.
368 */
369 $additional_blocks = apply_filters( 'suredonation_register_additional_blocks', [] );
370
371 if ( ! empty( $additional_blocks ) && is_array( $additional_blocks ) && count( $additional_blocks ) > 0 ) {
372 $blocks = [ ...$blocks, ...$additional_blocks ];
373 }
374
375 foreach ( $blocks as $block ) {
376 if ( ! is_array( $block ) || ! isset( $block['dir'] ) || ! isset( $block['namespace'] ) ) {
377 continue;
378 }
379 $block_files = glob( $block['dir'] );
380 if ( is_array( $block_files ) ) {
381 $this->register_block( $block_files, $block['namespace'], 'Block' );
382 }
383 }
384 }
385
386 /**
387 * Register blocks from directory.
388 *
389 * @param array<int, string> $blocks_dir Array of block file paths.
390 * @param string $block_namespace Block namespace.
391 * @param string $base Base class name.
392 * @return void
393 * @since 0.0.1
394 */
395 public function register_block( $blocks_dir, $block_namespace, $base ) {
396 if ( empty( $blocks_dir ) ) {
397 return;
398 }
399
400 foreach ( $blocks_dir as $filename ) {
401 // Skip base.php and register.php.
402 $basename = basename( $filename );
403 if ( 'base.php' === $basename || 'register.php' === $basename ) {
404 continue;
405 }
406
407 require_once $filename;
408
409 // Replace hyphens with underscores in directory name.
410 $classname = str_replace( '-', '_', basename( dirname( $filename ) ) );
411
412 // Convert to title case.
413 $classname = ucwords( $classname, '_' );
414
415 $full_class_name = $block_namespace . '\\' . $classname . '\\' . $base;
416
417 // Check if the class exists.
418 if ( class_exists( $full_class_name ) ) {
419 $block = new $full_class_name();
420
421 // Call register on the block object.
422 if ( method_exists( $block, 'register' ) ) {
423 $block->register();
424 }
425 }
426 }
427 }
428 }
429