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
suredonation / inc / api / settings-api.php

settings-api.php in SureDonation – Donation Forms, Fundraising Campaigns & Donor Management 1.6.1, at inc/api/settings-api.php

841 lines 23.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * General Settings REST API endpoints.
4 *
5 * @package SureDonation
6 */
7
8 namespace SureDonation\Inc\API;
9
10 use SureDonation\Inc\Emails\Email_Reports;
11 use SureDonation\Inc\Helper;
12 use SureDonation\Inc\Payments\Payment_Helper;
13 use WP_Error;
14 use WP_REST_Request;
15 use WP_REST_Response;
16 use WP_REST_Server;
17
18 // Exit if accessed directly.
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22
23 /**
24 * Settings API class.
25 *
26 * @since 0.0.1
27 */
28 class Settings_API {
29 /**
30 * Option key for email notifications within consolidated options.
31 *
32 * @since 0.0.1
33 */
34 public const EMAIL_OPTION_KEY = 'email_notifications';
35
36 /**
37 * Option key for AI settings within consolidated options.
38 *
39 * @since 1.0.0
40 */
41 public const AI_OPTION_KEY = 'ai_settings';
42
43 /**
44 * Option key for spam protection settings within consolidated options.
45 *
46 * @since 1.1.0
47 */
48 public const SPAM_OPTION_KEY = 'spam_protection_settings';
49
50 /**
51 * Option key for donor management settings within consolidated options.
52 *
53 * @since 1.0.0
54 */
55 public const DONOR_OPTION_KEY = 'donor_settings';
56
57 /**
58 * Get settings endpoints.
59 *
60 * @return array<string, mixed>
61 * @since 0.0.1
62 */
63 public function get_endpoints() {
64 return [
65 // Get currency data for block editor (public endpoint).
66 '/settings' => [
67 'methods' => WP_REST_Server::READABLE,
68 'callback' => [ $this, 'get_currency_settings' ],
69 'permission_callback' => '__return_true',
70 ],
71
72 // Get and update general settings.
73 '/settings/general' => [
74 [
75 'methods' => WP_REST_Server::READABLE,
76 'callback' => [ $this, 'get_settings' ],
77 'permission_callback' => [ $this, 'check_permissions' ],
78 ],
79 [
80 'methods' => WP_REST_Server::EDITABLE,
81 'callback' => [ $this, 'update_settings' ],
82 'permission_callback' => [ $this, 'check_permissions' ],
83 ],
84 ],
85
86 // Get available currencies.
87 '/settings/currencies' => [
88 'methods' => WP_REST_Server::READABLE,
89 'callback' => [ $this, 'get_currencies' ],
90 'permission_callback' => [ $this, 'check_permissions' ],
91 ],
92
93 // Email notifications are managed per-form via post meta.
94 // See inc/form-editor/assets.php for the form-level email system.
95 // AI settings.
96 '/settings/ai' => [
97 [
98 'methods' => WP_REST_Server::READABLE,
99 'callback' => [ $this, 'get_ai_settings' ],
100 'permission_callback' => [ $this, 'check_permissions' ],
101 ],
102 [
103 'methods' => WP_REST_Server::EDITABLE,
104 'callback' => [ $this, 'update_ai_settings' ],
105 'permission_callback' => [ $this, 'check_permissions' ],
106 ],
107 ],
108
109 // Spam protection settings.
110 '/settings/spam-protection' => [
111 [
112 'methods' => WP_REST_Server::READABLE,
113 'callback' => [ $this, 'get_spam_protection_settings' ],
114 'permission_callback' => [ $this, 'check_permissions' ],
115 ],
116 [
117 'methods' => WP_REST_Server::EDITABLE,
118 'callback' => [ $this, 'update_spam_protection_settings' ],
119 'permission_callback' => [ $this, 'check_permissions' ],
120 ],
121 ],
122
123 // Miscellaneous settings (usage tracking, etc.).
124 '/settings/misc' => [
125 [
126 'methods' => WP_REST_Server::READABLE,
127 'callback' => [ $this, 'get_misc_settings' ],
128 'permission_callback' => [ $this, 'check_permissions' ],
129 ],
130 [
131 'methods' => WP_REST_Server::EDITABLE,
132 'callback' => [ $this, 'update_misc_settings' ],
133 'permission_callback' => [ $this, 'check_permissions' ],
134 'args' => [
135 'usage_tracking' => [
136 'type' => 'boolean',
137 'sanitize_callback' => 'rest_sanitize_boolean',
138 ],
139 ],
140 ],
141 ],
142
143 // Donor management settings.
144 '/settings/donor' => [
145 [
146 'methods' => WP_REST_Server::READABLE,
147 'callback' => [ $this, 'get_donor_settings' ],
148 'permission_callback' => [ $this, 'check_permissions' ],
149 ],
150 [
151 'methods' => WP_REST_Server::EDITABLE,
152 'callback' => [ $this, 'update_donor_settings' ],
153 'permission_callback' => [ $this, 'check_permissions' ],
154 'args' => [
155 'create_wp_user' => [
156 'type' => 'boolean',
157 'sanitize_callback' => 'rest_sanitize_boolean',
158 ],
159 // Registered so the handler's (bool) cast receives a real
160 // boolean: a form-encoded "false" would otherwise cast to true
161 // and switch moderation ON when the admin asked for OFF.
162 'hold_donor_comments' => [
163 'type' => 'boolean',
164 'sanitize_callback' => 'rest_sanitize_boolean',
165 ],
166 ],
167 ],
168 ],
169
170 // Form validation default messages.
171 '/settings/validation' => [
172 [
173 'methods' => WP_REST_Server::READABLE,
174 'callback' => [ $this, 'get_validation_settings' ],
175 'permission_callback' => [ $this, 'check_permissions' ],
176 ],
177 [
178 'methods' => WP_REST_Server::EDITABLE,
179 'callback' => [ $this, 'update_validation_settings' ],
180 'permission_callback' => [ $this, 'check_permissions' ],
181 ],
182 ],
183
184 // Privacy settings (data retention, consent, privacy/terms fields).
185 '/settings/privacy' => [
186 [
187 'methods' => WP_REST_Server::READABLE,
188 'callback' => [ $this, 'get_privacy_settings' ],
189 'permission_callback' => [ $this, 'check_permissions' ],
190 ],
191 [
192 'methods' => WP_REST_Server::EDITABLE,
193 'callback' => [ $this, 'update_privacy_settings' ],
194 'permission_callback' => [ $this, 'check_permissions' ],
195 ],
196 ],
197
198 // Email Reports (weekly donation digest).
199 '/settings/email-reports' => [
200 [
201 'methods' => WP_REST_Server::READABLE,
202 'callback' => [ $this, 'get_email_reports_settings' ],
203 'permission_callback' => [ $this, 'check_permissions' ],
204 ],
205 [
206 'methods' => WP_REST_Server::EDITABLE,
207 'callback' => [ $this, 'update_email_reports_settings' ],
208 'permission_callback' => [ $this, 'check_permissions' ],
209 ],
210 ],
211
212 // Send this week's report now, to the addresses in the request.
213 '/settings/email-reports/test' => [
214 'methods' => WP_REST_Server::CREATABLE,
215 'callback' => [ $this, 'send_test_email_report' ],
216 'permission_callback' => [ $this, 'check_permissions' ],
217 ],
218 ];
219 }
220
221 /**
222 * Get the Email Reports settings (stored values merged over the defaults).
223 *
224 * @param WP_REST_Request $request Request object.
225 * @return WP_REST_Response
226 * @since 1.6.1
227 */
228 public function get_email_reports_settings( $request ) {
229 unset( $request ); // Unused parameter.
230
231 return new WP_REST_Response( self::email_reports_payload( Email_Reports::get_settings() ), 200 );
232 }
233
234 /**
235 * Update the Email Reports settings and (re)schedule the weekly send.
236 *
237 * Turning the report on with no deliverable address is refused with a
238 * 400 rather than quietly stored as off: the client's address check is
239 * looser than is_email(), and a success response would leave the screen
240 * showing the report as on while nothing is scheduled.
241 *
242 * @param WP_REST_Request $request Request object.
243 * @return WP_REST_Response
244 * @since 1.6.1
245 */
246 public function update_email_reports_settings( $request ) {
247 $params = $request->get_json_params();
248 $params = is_array( $params ) ? $params : [];
249
250 $wants_on = filter_var( $params['enabled'] ?? false, FILTER_VALIDATE_BOOLEAN );
251 $sanitized = Email_Reports::sanitize( $params );
252
253 if ( $wants_on && ! $sanitized['enabled'] ) {
254 return new WP_REST_Response(
255 [
256 'success' => false,
257 'code' => 'no_valid_recipient',
258 'message' => __( 'Enter at least one valid email address to turn on email reports.', 'suredonation' ),
259 ],
260 400
261 );
262 }
263
264 return new WP_REST_Response( self::email_reports_payload( Email_Reports::save( $params ) ), 200 );
265 }
266
267 /**
268 * The Email Reports response body: the stored settings plus the schedule
269 * state, so the screen can show when the next report goes out, or that
270 * none is queued.
271 *
272 * @param array<string, mixed> $settings Stored settings.
273 * @return array<string, mixed>
274 * @since 1.6.1
275 */
276 private static function email_reports_payload( $settings ) {
277 $next_run = Email_Reports::next_run();
278
279 return [
280 'success' => true,
281 'settings' => $settings,
282 'next_run' => $next_run,
283 'next_run_label' => null === $next_run
284 ? ''
285 : Helper::get_string_value( wp_date( Helper::get_string_value( get_option( 'date_format' ) ) . ' ' . Helper::get_string_value( get_option( 'time_format' ) ), $next_run ) ),
286 ];
287 }
288
289 /**
290 * Send this week's report immediately to the addresses in the request.
291 *
292 * Reads recipients from the request, not the stored settings, so an admin
293 * can preview before saving. Sends even when the week has no donations.
294 *
295 * @param WP_REST_Request $request Request object.
296 * @return WP_REST_Response
297 * @since 1.6.1
298 */
299 public function send_test_email_report( $request ) {
300 $params = $request->get_json_params();
301 $recipients = Email_Reports::parse_recipients( is_array( $params ) ? ( $params['recipients'] ?? '' ) : '' );
302
303 if ( [] === $recipients ) {
304 return new WP_REST_Response(
305 [
306 'success' => false,
307 'message' => __( 'Enter at least one valid email address.', 'suredonation' ),
308 ],
309 400
310 );
311 }
312
313 if ( ! Email_Reports::send_report( $recipients, true ) ) {
314 return new WP_REST_Response(
315 [
316 'success' => false,
317 'message' => __( 'The report could not be sent. Check your site’s email configuration.', 'suredonation' ),
318 ],
319 500
320 );
321 }
322
323 return new WP_REST_Response(
324 [
325 'success' => true,
326 'message' => __( 'Test report sent.', 'suredonation' ),
327 ],
328 200
329 );
330 }
331
332 /**
333 * Get the Privacy settings (stored values merged over the defaults).
334 *
335 * @param WP_REST_Request $request Request object.
336 * @return WP_REST_Response
337 * @since 1.2.0
338 */
339 public function get_privacy_settings( $request ) {
340 unset( $request ); // Unused parameter.
341
342 return new WP_REST_Response(
343 [
344 'success' => true,
345 'settings' => \SureDonation\Inc\Privacy\Privacy_Settings::get_settings(),
346 ],
347 200
348 );
349 }
350
351 /**
352 * Update the Privacy settings.
353 *
354 * @param WP_REST_Request $request Request object.
355 * @return WP_REST_Response
356 * @since 1.2.0
357 */
358 public function update_privacy_settings( $request ) {
359 $params = $request->get_json_params();
360 $sanitized = \SureDonation\Inc\Privacy\Privacy_Settings::sanitize( is_array( $params ) ? $params : [] );
361
362 Helper::update_suredonation_option( \SureDonation\Inc\Privacy\Privacy_Settings::OPTION_KEY, $sanitized );
363
364 return new WP_REST_Response(
365 [
366 'success' => true,
367 'settings' => $sanitized,
368 ],
369 200
370 );
371 }
372
373 /**
374 * Get the form-validation default messages.
375 *
376 * Returns the stored admin overrides merged over the translatable defaults
377 * so every configurable message always has a value in the editor.
378 *
379 * @param WP_REST_Request $request Request object.
380 * @return WP_REST_Response
381 * @since 1.1.0
382 */
383 public function get_validation_settings( $request ) {
384 unset( $request ); // Unused parameter.
385
386 $defaults = \SureDonation\Inc\Field_Validation::default_validation_messages();
387 $stored = Helper::get_suredonation_option( \SureDonation\Inc\Field_Validation::VALIDATION_MESSAGES_OPTION_KEY, [] );
388
389 return new WP_REST_Response(
390 [
391 'success' => true,
392 'settings' => wp_parse_args( is_array( $stored ) ? $stored : [], $defaults ),
393 ],
394 200
395 );
396 }
397
398 /**
399 * Update the form-validation default messages.
400 *
401 * @param WP_REST_Request $request Request object.
402 * @return WP_REST_Response
403 * @since 1.1.0
404 */
405 public function update_validation_settings( $request ) {
406 $current = Helper::get_suredonation_option( \SureDonation\Inc\Field_Validation::VALIDATION_MESSAGES_OPTION_KEY, [] );
407
408 if ( ! is_array( $current ) ) {
409 $current = [];
410 }
411
412 /**
413 * Filter the list of allowed validation-message keys.
414 *
415 * Lets extensions register additional message keys for their own field
416 * types, mirroring the `suredonation.settings.tab.validationFields` and
417 * `suredonation.settings.tab.requiredValidationFields` JS filters.
418 *
419 * @since 1.1.0
420 * @param array<int, string> $keys Allowed message keys.
421 */
422 $allowed_keys = apply_filters(
423 'suredonation_validation_message_keys',
424 array_keys( \SureDonation\Inc\Field_Validation::default_validation_messages() )
425 );
426
427 foreach ( $allowed_keys as $key ) {
428 if ( ! is_string( $key ) ) {
429 continue;
430 }
431
432 $value = $request->get_param( $key );
433 // Guard against non-scalar input (array/object) which would make
434 // sanitize_text_field() emit a warning / type error on PHP 8.1+.
435 if ( null !== $value && is_scalar( $value ) ) {
436 $current[ $key ] = sanitize_text_field( (string) $value );
437 }
438 }
439
440 Helper::update_suredonation_option( \SureDonation\Inc\Field_Validation::VALIDATION_MESSAGES_OPTION_KEY, $current );
441
442 return new WP_REST_Response(
443 [
444 'success' => true,
445 'message' => __( 'Form validation settings saved', 'suredonation' ),
446 ],
447 200
448 );
449 }
450
451 /**
452 * Get currency settings for block editor.
453 *
454 * Returns minimal currency data needed for frontend/block previews.
455 *
456 * @param WP_REST_Request $request Request object.
457 * @return WP_REST_Response Response object.
458 * @since 0.0.1
459 */
460 public function get_currency_settings( $request ) {
461 unset( $request ); // Unused parameter.
462
463 $currency = Payment_Helper::get_currency();
464
465 return new WP_REST_Response(
466 [
467 'currency' => $currency,
468 'currencySymbol' => Payment_Helper::get_currency_symbol( $currency ),
469 'isZeroDecimal' => Payment_Helper::is_zero_decimal_currency( $currency ),
470 ],
471 200
472 );
473 }
474
475 /**
476 * Get general settings.
477 *
478 * @param WP_REST_Request $request Request object.
479 * @return WP_REST_Response Response object.
480 * @since 0.0.1
481 */
482 public function get_settings( $request ) {
483 unset( $request ); // Unused parameter.
484
485 $settings = Payment_Helper::get_all_payment_settings();
486
487 return new WP_REST_Response(
488 [
489 'success' => true,
490 'settings' => [
491 'currency' => $settings['currency'] ?? 'USD',
492 'payment_mode' => $settings['payment_mode'] ?? 'test',
493 'currency_sign_position' => Payment_Helper::get_currency_sign_position(),
494 ],
495 ],
496 200
497 );
498 }
499
500 /**
501 * Update general settings.
502 *
503 * @param WP_REST_Request $request Request object.
504 * @return WP_REST_Response|WP_Error Response object.
505 * @since 0.0.1
506 */
507 public function update_settings( $request ) {
508 $params = $request->get_json_params();
509
510 if ( empty( $params ) ) {
511 return new WP_Error(
512 'invalid_settings',
513 __( 'Invalid settings provided', 'suredonation' ),
514 [ 'status' => 400 ]
515 );
516 }
517
518 $current_settings = Payment_Helper::get_all_payment_settings();
519
520 // Update currency if provided.
521 if ( isset( $params['currency'] ) ) {
522 $currency = strtoupper( sanitize_text_field( $params['currency'] ) );
523
524 // Validate currency.
525 $valid_currencies = array_keys( Payment_Helper::get_all_currencies_data() );
526 if ( in_array( $currency, $valid_currencies, true ) ) {
527 $current_settings['currency'] = $currency;
528 }
529 }
530
531 // Update payment mode if provided.
532 if ( isset( $params['payment_mode'] ) ) {
533 $mode = sanitize_text_field( $params['payment_mode'] );
534 if ( in_array( $mode, [ 'test', 'live' ], true ) ) {
535 $current_settings['payment_mode'] = $mode;
536 }
537 }
538
539 // Update currency sign position if provided.
540 if ( isset( $params['currency_sign_position'] ) ) {
541 $position = sanitize_text_field( $params['currency_sign_position'] );
542 if ( in_array( $position, Payment_Helper::ALLOWED_SIGN_POSITIONS, true ) ) {
543 $current_settings['currency_sign_position'] = $position;
544 }
545 }
546
547 $success = Payment_Helper::update_all_payment_settings( $current_settings );
548
549 if ( ! $success ) {
550 return new WP_Error(
551 'update_failed',
552 __( 'Failed to update settings', 'suredonation' ),
553 [ 'status' => 500 ]
554 );
555 }
556
557 return new WP_REST_Response(
558 [
559 'success' => true,
560 'message' => __( 'Settings updated successfully', 'suredonation' ),
561 ],
562 200
563 );
564 }
565
566 /**
567 * Get available currencies.
568 *
569 * @param WP_REST_Request $request Request object.
570 * @return WP_REST_Response Response object.
571 * @since 0.0.1
572 */
573 public function get_currencies( $request ) {
574 unset( $request ); // Unused parameter.
575
576 return new WP_REST_Response(
577 [
578 'success' => true,
579 'currencies' => Payment_Helper::get_currencies_list(),
580 ],
581 200
582 );
583 }
584
585 /**
586 * Get AI settings.
587 *
588 * @param WP_REST_Request $request Request object.
589 * @return WP_REST_Response Response object.
590 * @since 1.0.0
591 */
592 public function get_ai_settings( $request ) {
593 unset( $request ); // Unused parameter.
594
595 $defaults = [
596 'enable_abilities' => false,
597 'allow_updates' => false,
598 'allow_delete' => false,
599 'mcp_server' => false,
600 ];
601 $settings = Helper::get_suredonation_option( self::AI_OPTION_KEY, [] );
602
603 return new WP_REST_Response(
604 [
605 'success' => true,
606 'settings' => wp_parse_args( is_array( $settings ) ? $settings : [], $defaults ),
607 ],
608 200
609 );
610 }
611
612 /**
613 * Update AI settings.
614 *
615 * @param WP_REST_Request $request Request object.
616 * @return WP_REST_Response Response object.
617 * @since 1.0.0
618 */
619 public function update_ai_settings( $request ) {
620 $params = $request->get_json_params();
621 $current = Helper::get_suredonation_option( self::AI_OPTION_KEY, [] );
622
623 if ( ! is_array( $current ) ) {
624 $current = [];
625 }
626
627 $allowed = [ 'enable_abilities', 'allow_updates', 'allow_delete', 'mcp_server' ];
628 foreach ( $allowed as $key ) {
629 if ( isset( $params[ $key ] ) ) {
630 $current[ $key ] = (bool) $params[ $key ];
631 }
632 }
633
634 Helper::update_suredonation_option( self::AI_OPTION_KEY, $current );
635
636 return new WP_REST_Response(
637 [
638 'success' => true,
639 'message' => __( 'AI settings saved', 'suredonation' ),
640 ],
641 200
642 );
643 }
644
645 /**
646 * Get spam protection settings.
647 *
648 * @param WP_REST_Request $request Request object.
649 * @return WP_REST_Response Response object.
650 * @since 1.1.0
651 */
652 public function get_spam_protection_settings( $request ) {
653 unset( $request ); // Unused parameter.
654
655 $defaults = [
656 'honeypot' => false,
657 ];
658 $settings = Helper::get_suredonation_option( self::SPAM_OPTION_KEY, [] );
659
660 return new WP_REST_Response(
661 [
662 'success' => true,
663 'settings' => wp_parse_args( is_array( $settings ) ? $settings : [], $defaults ),
664 ],
665 200
666 );
667 }
668
669 /**
670 * Update spam protection settings.
671 *
672 * @param WP_REST_Request $request Request object.
673 * @return WP_REST_Response Response object.
674 * @since 1.1.0
675 */
676 public function update_spam_protection_settings( $request ) {
677 $current = Helper::get_suredonation_option( self::SPAM_OPTION_KEY, [] );
678
679 if ( ! is_array( $current ) ) {
680 $current = [];
681 }
682
683 // Read each setting via get_param() so the endpoint accepts JSON, body,
684 // or query params (matches the sibling /settings/* update handlers).
685 $allowed = [ 'honeypot' ];
686 foreach ( $allowed as $key ) {
687 $value = $request->get_param( $key );
688 if ( null !== $value ) {
689 $current[ $key ] = (bool) $value;
690 }
691 }
692
693 Helper::update_suredonation_option( self::SPAM_OPTION_KEY, $current );
694
695 return new WP_REST_Response(
696 [
697 'success' => true,
698 'message' => __( 'Spam protection settings saved', 'suredonation' ),
699 ],
700 200
701 );
702 }
703
704 /**
705 * Get miscellaneous settings.
706 *
707 * @param WP_REST_Request $request Request object.
708 * @return WP_REST_Response Response object.
709 * @since 1.0.0
710 */
711 public function get_misc_settings( $request ) {
712 unset( $request ); // Unused parameter.
713
714 return new WP_REST_Response(
715 [
716 'success' => true,
717 'settings' => [
718 // Site option - the BSF Analytics library reads this via
719 // get_site_option(), so the toggle must use the same
720 // scope to stay in sync on multisite.
721 'usage_tracking' => 'yes' === get_site_option( 'suredonation_usage_optin', false ),
722 ],
723 ],
724 200
725 );
726 }
727
728 /**
729 * Update miscellaneous settings.
730 *
731 * Stores the usage-tracking opt-in as the standalone 'yes'/'no'
732 * suredonation_usage_optin option read by the BSF Analytics library.
733 *
734 * @param WP_REST_Request $request Request object.
735 * @return WP_REST_Response Response object.
736 * @since 1.0.0
737 */
738 public function update_misc_settings( $request ) {
739 $usage_tracking = $request->get_param( 'usage_tracking' );
740
741 if ( null !== $usage_tracking ) {
742 if ( $usage_tracking ) {
743 update_site_option( 'suredonation_usage_optin', 'yes' );
744 } else {
745 // Mirror the library's optout() side effects (see
746 // class-bsf-analytics.php) so the cross-product notice
747 // throttle and the send-check transient stay consistent.
748 update_site_option( 'suredonation_usage_optin', 'no' );
749 update_site_option( 'bsf_usage_last_displayed_time', time() );
750 delete_site_transient( 'bsf_usage_track' );
751 }
752 }
753
754 return new WP_REST_Response(
755 [
756 'success' => true,
757 'message' => __( 'Settings saved', 'suredonation' ),
758 ],
759 200
760 );
761 }
762
763 /**
764 * Get donor management settings.
765 *
766 * @param WP_REST_Request $request Request object.
767 * @return WP_REST_Response Response object.
768 * @since 1.0.0
769 */
770 public function get_donor_settings( $request ) {
771 unset( $request ); // Unused parameter.
772
773 $donor_settings = Helper::get_suredonation_option( self::DONOR_OPTION_KEY, [] );
774 if ( ! is_array( $donor_settings ) ) {
775 $donor_settings = [];
776 }
777
778 return new WP_REST_Response(
779 [
780 'success' => true,
781 'settings' => [
782 // Off by default: guest donations never auto-create WP user accounts.
783 'create_wp_user' => ! empty( $donor_settings['create_wp_user'] ),
784 // Off by default: donor comments publish as soon as the donation
785 // completes, matching GiveWP and Charitable out of the box. Turning
786 // it on holds new comments as `pending` for review instead.
787 'hold_donor_comments' => ! empty( $donor_settings['hold_donor_comments'] ),
788 ],
789 ],
790 200
791 );
792 }
793
794 /**
795 * Update donor management settings.
796 *
797 * @param WP_REST_Request $request Request object.
798 * @return WP_REST_Response Response object.
799 * @since 1.0.0
800 */
801 public function update_donor_settings( $request ) {
802 $donor_settings = Helper::get_suredonation_option( self::DONOR_OPTION_KEY, [] );
803 if ( ! is_array( $donor_settings ) ) {
804 $donor_settings = [];
805 }
806
807 // Read each setting via get_param() so the endpoint accepts JSON, body,
808 // or query params (matches the sibling /settings/* update handlers).
809 $changed = false;
810 foreach ( [ 'create_wp_user', 'hold_donor_comments' ] as $key ) {
811 $value = $request->get_param( $key );
812 if ( null !== $value ) {
813 $donor_settings[ $key ] = (bool) $value;
814 $changed = true;
815 }
816 }
817
818 if ( $changed ) {
819 Helper::update_suredonation_option( self::DONOR_OPTION_KEY, $donor_settings );
820 }
821
822 return new WP_REST_Response(
823 [
824 'success' => true,
825 'message' => __( 'Settings saved', 'suredonation' ),
826 ],
827 200
828 );
829 }
830
831 /**
832 * Check if user has permission to manage settings.
833 *
834 * @return bool True if user has permission.
835 * @since 0.0.1
836 */
837 public function check_permissions() {
838 return current_user_can( 'manage_options' );
839 }
840 }
841