PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.6.0
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.6.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 / import / givewp / session.php

session.php in SureDonation – Donation Forms, Fundraising Campaigns & Donor Management 1.6.0, at inc/import/givewp/session.php

246 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Migration session store.
4 *
5 * Wraps a transient with typed accessors so the rest of the importer never
6 * touches transient keys directly. Each session is keyed by a UUID that the
7 * REST layer hands back to the client and expects back on subsequent /batch
8 * calls.
9 *
10 * @package SureDonation
11 */
12
13 namespace SureDonation\Inc\Import\Givewp;
14
15 use SureDonation\Inc\Traits\Get_Instance;
16
17 // Exit if accessed directly.
18 defined( 'ABSPATH' ) || exit;
19
20 /**
21 * Session class.
22 *
23 * @since 1.0.0
24 */
25 class Session {
26 use Get_Instance;
27
28 /**
29 * Transient key prefix.
30 */
31 const TRANSIENT_PREFIX = 'suredonation_givewp_import_';
32
33 /**
34 * Session TTL.
35 *
36 * Long enough to survive batch gaps and brief page reloads, short enough
37 * that abandoned sessions don't pile up indefinitely.
38 */
39 const TTL = HOUR_IN_SECONDS;
40
41 /**
42 * Create a new import session and return its progress payload.
43 *
44 * Options shape:
45 * - campaign_ids: int[] — GiveWP form IDs selected in the preview UI.
46 * - include_standalone_donors: bool — whether to also import donors
47 * that have no associated payments (Pro phase, off by default).
48 *
49 * Campaigns + donations phases are always registered (the user picked
50 * at least one campaign on the preview step). Subscriptions is injected
51 * by the Pro plugin via the phases filter when the give_subscriptions
52 * table exists. Standalone donors is opt-in via the option above.
53 *
54 * @param array $options Session options.
55 * @return array Newly created progress payload (including generated import_id) or empty array if no phases are selected.
56 * @since 1.0.0
57 */
58 public function create( $options ) {
59 $options = is_array( $options ) ? $options : [];
60
61 $defaults = [
62 'campaign_ids' => [],
63 'include_standalone_donors' => false,
64 ];
65
66 $options = wp_parse_args( $options, $defaults );
67
68 $options['campaign_ids'] = is_array( $options['campaign_ids'] )
69 ? array_values( array_unique( array_filter( array_map( 'absint', $options['campaign_ids'] ) ) ) )
70 : [];
71 $options['include_standalone_donors'] = (bool) $options['include_standalone_donors'];
72
73 if ( empty( $options['campaign_ids'] ) ) {
74 return [];
75 }
76
77 // Campaigns + donations always run; the user opted in to those by
78 // picking campaigns on the preview step. Pro injects subscriptions
79 // and (conditionally) standalone_donors via the filter below.
80 $phases = [ 'campaigns', 'donations' ];
81
82 /**
83 * Filter the phases that a GiveWP migration session will run.
84 *
85 * Pro reads $options['include_standalone_donors'] when deciding
86 * whether to inject the standalone_donors phase. The subscriptions
87 * phase is injected unconditionally when the give_subscriptions
88 * table exists — it's still campaign-scoped because the mapper
89 * filters by $progress['options']['campaign_ids'].
90 *
91 * @param array $phases Default phases (campaigns + donations).
92 * @param array $options Resolved options for this session.
93 * @since 1.0.0
94 */
95 $phases = apply_filters( 'suredonation_import_givewp_phases', $phases, $options );
96 $phases = is_array( $phases ) ? array_values( array_unique( array_filter( array_map( 'strval', $phases ) ) ) ) : [];
97
98 if ( empty( $phases ) ) {
99 return [];
100 }
101
102 $progress = [
103 'import_id' => wp_generate_uuid4(),
104 'started_at' => current_time( 'mysql', true ),
105 'started_by' => get_current_user_id(),
106 'options' => $options,
107 'phases' => $phases,
108 'current_phase' => 0,
109 'offset' => 0,
110 'campaign_map' => [],
111 'donor_map' => [],
112 'status' => 'running',
113 'results' => $this->empty_results( $phases ),
114 ];
115
116 $this->put( $progress['import_id'], $progress );
117
118 /**
119 * Fires after a new GiveWP migration session has been created.
120 *
121 * @param string $import_id Session UUID.
122 * @param array $progress Full progress payload.
123 * @since 1.0.0
124 */
125 do_action( 'suredonation_import_givewp_session_created', $progress['import_id'], $progress );
126
127 return $progress;
128 }
129
130 /**
131 * Load progress for an existing import session.
132 *
133 * @param string $import_id Session UUID.
134 * @return array|false Progress payload, or false if the transient is gone.
135 * @since 1.0.0
136 */
137 public function get( $import_id ) {
138 $import_id = $this->sanitize_id( $import_id );
139 if ( '' === $import_id ) {
140 return false;
141 }
142
143 $progress = get_transient( self::TRANSIENT_PREFIX . $import_id );
144 return is_array( $progress ) ? $progress : false;
145 }
146
147 /**
148 * Persist updated progress to its transient.
149 *
150 * @param string $import_id Session UUID.
151 * @param array $progress Progress payload.
152 * @return bool
153 * @since 1.0.0
154 */
155 public function put( $import_id, $progress ) {
156 $import_id = $this->sanitize_id( $import_id );
157 if ( '' === $import_id || ! is_array( $progress ) ) {
158 return false;
159 }
160
161 return (bool) set_transient( self::TRANSIENT_PREFIX . $import_id, $progress, self::TTL );
162 }
163
164 /**
165 * Delete a session.
166 *
167 * @param string $import_id Session UUID.
168 * @return bool
169 * @since 1.0.0
170 */
171 public function delete( $import_id ) {
172 $import_id = $this->sanitize_id( $import_id );
173 if ( '' === $import_id ) {
174 return false;
175 }
176
177 return (bool) delete_transient( self::TRANSIENT_PREFIX . $import_id );
178 }
179
180 /**
181 * Build a zeroed results structure for the given phases.
182 *
183 * @param array $phases Phase names.
184 * @return array
185 * @since 1.0.0
186 */
187 private function empty_results( $phases ) {
188 $schema = [
189 'campaigns' => [
190 'imported' => 0,
191 'skipped' => 0,
192 'errors' => 0,
193 'error_log' => [],
194 ],
195 'donations' => [
196 'imported' => 0,
197 'skipped' => 0,
198 'errors' => 0,
199 'error_log' => [],
200 'gateway_breakdown' => [],
201 ],
202 'subscriptions' => [
203 'imported_live' => 0,
204 'imported_historical' => 0,
205 'skipped' => 0,
206 'errors' => 0,
207 'error_log' => [],
208 ],
209 'standalone_donors' => [
210 'imported' => 0,
211 'skipped' => 0,
212 'errors' => 0,
213 'error_log' => [],
214 ],
215 ];
216
217 $out = [];
218 foreach ( $phases as $phase ) {
219 if ( isset( $schema[ $phase ] ) ) {
220 $out[ $phase ] = $schema[ $phase ];
221 }
222 }
223 return $out;
224 }
225
226 /**
227 * Sanitize an incoming import-id string.
228 *
229 * @param mixed $import_id Raw input.
230 * @return string Empty string if not a plausible UUID.
231 * @since 1.0.0
232 */
233 private function sanitize_id( $import_id ) {
234 if ( ! is_string( $import_id ) ) {
235 return '';
236 }
237 $import_id = sanitize_text_field( $import_id );
238
239 if ( ! preg_match( '/^[a-f0-9-]{8,64}$/i', $import_id ) ) {
240 return '';
241 }
242
243 return $import_id;
244 }
245 }
246