PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.1.1
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.1.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 / onboarding-api.php

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

239 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Onboarding REST endpoints.
4 *
5 * Routes (under `suredonation/v1`):
6 * - GET /onboarding/get-status — return { completed: 'yes'|'no' }
7 * - POST /onboarding/set-status — write completion + optional analytics
8 * - POST /onboarding/create-campaign — create a draft suredonation_cmpgn
9 * - POST /onboarding/user-details — persist lead capture (free-only step)
10 *
11 * @package SureDonation
12 */
13
14 namespace SureDonation\Inc\API;
15
16 use SureDonation\Inc\Campaigns\Campaign_Cpt;
17 use SureDonation\Inc\Helper;
18 use SureDonation\Inc\Onboarding;
19 use WP_Error;
20 use WP_REST_Request;
21 use WP_REST_Response;
22 use WP_REST_Server;
23
24 if ( ! defined( 'ABSPATH' ) ) {
25 exit;
26 }
27
28 /**
29 * Onboarding REST API endpoints.
30 *
31 * @since 1.0.0
32 */
33 class Onboarding_API {
34 /**
35 * Allowed goal types for the create-campaign endpoint.
36 *
37 * @since 1.0.0
38 * @var array<int,string>
39 */
40 private const GOAL_TYPES = [ 'raised_amount', 'donation_count' ];
41
42 /**
43 * Return endpoint definitions for Rest_Api to register.
44 *
45 * @return array<string,mixed>
46 * @since 1.0.0
47 */
48 public function get_endpoints() {
49 return [
50 '/onboarding/get-status' => [
51 'methods' => WP_REST_Server::READABLE,
52 'callback' => [ $this, 'get_status' ],
53 'permission_callback' => [ $this, 'check_permissions' ],
54 ],
55 '/onboarding/set-status' => [
56 'methods' => WP_REST_Server::EDITABLE,
57 'callback' => [ $this, 'set_status' ],
58 'permission_callback' => [ $this, 'check_permissions' ],
59 ],
60 '/onboarding/create-campaign' => [
61 'methods' => WP_REST_Server::EDITABLE,
62 'callback' => [ $this, 'create_campaign' ],
63 'permission_callback' => [ $this, 'check_permissions' ],
64 ],
65 '/onboarding/user-details' => [
66 'methods' => WP_REST_Server::EDITABLE,
67 'callback' => [ $this, 'save_user_details' ],
68 'permission_callback' => [ $this, 'check_permissions' ],
69 ],
70 ];
71 }
72
73 /**
74 * Permission gate.
75 *
76 * @return bool
77 * @since 1.0.0
78 */
79 public function check_permissions() {
80 return current_user_can( 'manage_options' );
81 }
82
83 /**
84 * GET /onboarding/get-status.
85 *
86 * @return WP_REST_Response
87 * @since 1.0.0
88 */
89 public function get_status() {
90 return new WP_REST_Response(
91 [
92 'completed' => Onboarding::get_instance()->is_completed() ? 'yes' : 'no',
93 ]
94 );
95 }
96
97 /**
98 * POST /onboarding/set-status.
99 *
100 * @param WP_REST_Request $request Request.
101 * @return WP_REST_Response
102 * @since 1.0.0
103 */
104 public function set_status( $request ) {
105 $completed = $request->get_param( 'completed' );
106 Onboarding::get_instance()->set_completed( 'yes' === $completed ? 'yes' : 'no' );
107
108 return new WP_REST_Response( [ 'success' => true ] );
109 }
110
111 /**
112 * POST /onboarding/create-campaign.
113 *
114 * Creates a draft campaign post + writes its meta. Returns the new
115 * campaign id + edit URL so the JS can persist it in onboarding state.
116 *
117 * @param WP_REST_Request $request Request.
118 * @return WP_REST_Response|WP_Error
119 * @since 1.0.0
120 */
121 public function create_campaign( $request ) {
122 $name = sanitize_text_field( (string) $request->get_param( 'name' ) );
123 $goal_type = (string) $request->get_param( 'goal_type' );
124 $description = wp_kses_post( (string) $request->get_param( 'description' ) );
125
126 // Clamp to a non-negative, finite, sane range. The JS already
127 // validates this, but the endpoint is callable directly by any
128 // manage_options user and shouldn't trust client-side bounds.
129 $goal_amount = (float) $request->get_param( 'goal_amount' );
130 if ( ! is_finite( $goal_amount ) || $goal_amount < 0 ) {
131 $goal_amount = 0.0;
132 }
133 // Cap at 1e9 so a stray "1e308" can't poison campaign meta.
134 $goal_amount = min( $goal_amount, 1000000000.0 );
135
136 if ( '' === trim( $name ) ) {
137 return new WP_Error(
138 'suredonation_campaign_name_required',
139 __( 'Campaign name is required.', 'suredonation' ),
140 [ 'status' => 400 ]
141 );
142 }
143
144 if ( ! in_array( $goal_type, self::GOAL_TYPES, true ) ) {
145 $goal_type = 'raised_amount';
146 }
147
148 $result = wp_insert_post(
149 [
150 'post_type' => Campaign_Cpt::POST_TYPE,
151 'post_status' => 'draft',
152 'post_title' => $name,
153 'post_excerpt' => $description,
154 'post_author' => get_current_user_id(),
155 ],
156 true
157 );
158
159 if ( is_wp_error( $result ) ) {
160 return new WP_Error(
161 'suredonation_campaign_create_failed',
162 $result->get_error_message(),
163 [ 'status' => 500 ]
164 );
165 }
166
167 $campaign_id = (int) $result;
168
169 if ( $campaign_id <= 0 ) {
170 return new WP_Error(
171 'suredonation_campaign_create_failed',
172 __( 'Could not create the campaign.', 'suredonation' ),
173 [ 'status' => 500 ]
174 );
175 }
176
177 Helper::update_campaign_meta(
178 $campaign_id,
179 [
180 'goal_type' => $goal_type,
181 'goal_amount' => $goal_amount,
182 ]
183 );
184
185 return new WP_REST_Response(
186 [
187 'success' => true,
188 'campaign_id' => $campaign_id,
189 'edit_url' => admin_url( 'admin.php?page=suredonation#/campaigns/' . $campaign_id ),
190 ]
191 );
192 }
193
194 /**
195 * POST /onboarding/user-details.
196 *
197 * Stores the lead-capture payload under suredonation_options so we
198 * don't re-prompt on subsequent setup passes.
199 *
200 * @param WP_REST_Request $request Request.
201 * @return WP_REST_Response
202 * @since 1.0.0
203 */
204 public function save_user_details( $request ) {
205 $payload = [
206 'first_name' => sanitize_text_field( (string) $request->get_param( 'first_name' ) ),
207 'last_name' => sanitize_text_field( (string) $request->get_param( 'last_name' ) ),
208 'email' => sanitize_email( (string) $request->get_param( 'email' ) ),
209 'opted_in' => (bool) $request->get_param( 'opted_in' ),
210 ];
211
212 Onboarding::get_instance()->set_user_details( $payload );
213
214 // Persist the usage-tracking opt-in as its own option so other
215 // plugin code (analytics, telemetry pings) can check it without
216 // loading the consolidated onboarding details. Site option — the
217 // BSF Analytics library reads it via get_site_option(), so the
218 // write must use the same scope to stay in sync on multisite.
219 update_site_option(
220 'suredonation_usage_optin',
221 $payload['opted_in'] ? 'yes' : 'no'
222 );
223
224 /**
225 * Fires after onboarding lead-capture details are persisted.
226 *
227 * Listeners (e.g. Pro analytics) can forward the payload to a
228 * metrics endpoint when `opted_in` is true.
229 *
230 * @since 1.0.0
231 *
232 * @param array<string,mixed> $payload Sanitised payload.
233 */
234 do_action( 'suredonation_onboarding_user_details_saved', $payload );
235
236 return new WP_REST_Response( [ 'success' => true ] );
237 }
238 }
239