PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / 1.1.2
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management v1.1.2
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 / importer.php

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

203 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * GiveWP migration orchestrator.
4 *
5 * Walks a session through its phases by delegating each batch to the
6 * appropriate phase mapper. Mappers are registered via the filter
7 * `suredonation_import_givewp_phase_mappers` so the Pro plugin can plug
8 * in subscription / standalone-donor mappers without Free knowing
9 * about them.
10 *
11 * @package SureDonation
12 */
13
14 namespace SureDonation\Inc\Import\Givewp;
15
16 use SureDonation\Inc\Traits\Get_Instance;
17
18 // Exit if accessed directly.
19 defined( 'ABSPATH' ) || exit;
20
21 /**
22 * Importer class.
23 *
24 * @since 1.0.0
25 */
26 class Importer {
27 use Get_Instance;
28
29 /**
30 * Records processed per AJAX batch. Matches Charitable's 25.
31 */
32 const BATCH_SIZE = 25;
33
34 /**
35 * Get pre-flight counts and per-gateway breakdown.
36 *
37 * @return array{has_data:bool, give_version:?string, counts:array<string,int>, gateway_breakdown:array<int,array<string,mixed>>}
38 * @since 1.0.0
39 */
40 public function get_counts() {
41 $source = Source::get_instance();
42
43 $gateway_breakdown = [];
44 foreach ( $source->get_gateway_breakdown() as $row ) {
45 $slug = isset( $row['slug'] ) ? (string) $row['slug'] : '';
46 $count = isset( $row['count'] ) ? (int) $row['count'] : 0;
47 $sd_slug = Status_Map::map_gateway( $slug );
48 $live = Status_Map::is_gateway_live( $sd_slug );
49 $sub_live = Status_Map::is_subscription_handler_live( $sd_slug );
50 $gateway_breakdown[] = [
51 'slug' => $slug,
52 'sd_slug' => $sd_slug,
53 'count' => $count,
54 'live' => $live,
55 'subscription_handler_live' => $sub_live,
56 ];
57 }
58
59 return [
60 'has_data' => $source->has_givewp_data(),
61 'give_version' => $source->get_givewp_version(),
62 'counts' => [
63 'campaigns' => $source->get_form_count(),
64 'donations' => $source->get_payment_count(),
65 'subscriptions' => $source->get_subscription_count(),
66 'standalone_donors' => $source->get_standalone_donor_count(),
67 ],
68 'gateway_breakdown' => $gateway_breakdown,
69 ];
70 }
71
72 /**
73 * Run the next batch for a given session.
74 *
75 * Loads progress, dispatches the current phase to its mapper, persists
76 * progress, and advances the phase index when the mapper signals the
77 * phase is exhausted (processed < batch size).
78 *
79 * @param string $import_id Session UUID.
80 * @return array|false Updated progress payload, or false if the session is missing or no longer running.
81 * @since 1.0.0
82 */
83 public function run_batch( $import_id ) {
84 $session = Session::get_instance();
85 $progress = $session->get( $import_id );
86
87 if ( ! is_array( $progress ) || empty( $progress['status'] ) || 'running' !== $progress['status'] ) {
88 return false;
89 }
90
91 $current_index = isset( $progress['current_phase'] ) ? (int) $progress['current_phase'] : 0;
92 $phases = isset( $progress['phases'] ) && is_array( $progress['phases'] ) ? $progress['phases'] : [];
93
94 if ( $current_index >= count( $phases ) ) {
95 $progress['status'] = 'complete';
96 $session->put( $import_id, $progress );
97 $this->fire_batch_complete( $import_id, $progress );
98 return $progress;
99 }
100
101 $phase = (string) $phases[ $current_index ];
102 $offset = isset( $progress['offset'] ) ? (int) $progress['offset'] : 0;
103
104 $mapper = $this->get_phase_mapper( $phase );
105
106 try {
107 if ( ! $mapper || ! is_callable( [ $mapper, 'process_batch' ] ) ) {
108 // Unknown phase — skip it and advance so the session never stalls.
109 $progress['current_phase'] = $current_index + 1;
110 $progress['offset'] = 0;
111 } else {
112 $processed = (int) $mapper->process_batch( $progress, $offset );
113
114 if ( $processed < self::BATCH_SIZE ) {
115 $progress['current_phase'] = $current_index + 1;
116 $progress['offset'] = 0;
117 } else {
118 $progress['offset'] = $offset + $processed;
119 }
120 }
121 } catch ( \Throwable $t ) {
122 // Whole-batch failure (mapper threw something the per-record
123 // try/catch didn't catch, or a fatal in the dispatcher).
124 // Mark the session as failed so the import history doesn't
125 // sit at 'running' forever, persist the cause for diagnosis,
126 // and short-circuit further phase advancement.
127 $progress['status'] = 'failed';
128 $progress['completed_at'] = current_time( 'mysql', true );
129 $progress['error'] = $t->getMessage();
130 $session->put( $import_id, $progress );
131 $this->fire_batch_complete( $import_id, $progress );
132 return $progress;
133 }
134
135 if ( $progress['current_phase'] >= count( $phases ) ) {
136 $progress['status'] = 'complete';
137 $progress['completed_at'] = current_time( 'mysql', true );
138 }
139
140 $session->put( $import_id, $progress );
141
142 $this->fire_batch_complete( $import_id, $progress );
143
144 return $progress;
145 }
146
147 /**
148 * Resolve the mapper instance responsible for a given phase.
149 *
150 * @param string $phase Phase name.
151 * @return object|null Mapper with a process_batch( &$progress, $offset ) method.
152 * @since 1.0.0
153 */
154 private function get_phase_mapper( $phase ) {
155 $mappers = [
156 'campaigns' => Campaign_Mapper::class,
157 'donations' => Donation_Mapper::class,
158 ];
159
160 /**
161 * Filter the map of phase => mapper class.
162 *
163 * Pro plugin registers `subscriptions` and `standalone_donors` mappers
164 * here. Each class must expose a public process_batch( &$progress, $offset )
165 * method that returns the number of source records processed in the batch.
166 *
167 * @param array<string,string> $mappers Phase => mapper class.
168 * @since 1.0.0
169 */
170 $mappers = apply_filters( 'suredonation_import_givewp_phase_mappers', $mappers );
171
172 if ( ! is_array( $mappers ) || ! isset( $mappers[ $phase ] ) ) {
173 return null;
174 }
175
176 $class = (string) $mappers[ $phase ];
177 if ( ! class_exists( $class ) || ! is_callable( [ $class, 'get_instance' ] ) ) {
178 return null;
179 }
180
181 return call_user_func( [ $class, 'get_instance' ] );
182 }
183
184 /**
185 * Fire the batch-complete action so observers (e.g. Pro history recorder) can persist snapshots.
186 *
187 * @param string $import_id Session UUID.
188 * @param array $progress Progress payload after this batch.
189 * @return void
190 * @since 1.0.0
191 */
192 private function fire_batch_complete( $import_id, $progress ) {
193 /**
194 * Fires after each migration batch is processed (running or final).
195 *
196 * @param string $import_id Session UUID.
197 * @param array $progress Updated progress payload.
198 * @since 1.0.0
199 */
200 do_action( 'suredonation_import_givewp_batch_complete', $import_id, $progress );
201 }
202 }
203