PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.13
1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 1.7.0.9 All 211 releases
← All changes | includes/admin/import-items/class-charitable-import-items.php +756 -387 1.7.0.11 → 1.8.13 View file →
@@ -3,11 +3,12 @@
3 3 * This class is responsible for adding the Charitable admin pages.
4 4 *
5 5 * @package Charitable/Classes/Charitable_Import_Items
6 6 * @author David Bisset
7 - * @copyright Copyright (c) 2022, WP Charitable LLC
7 + * @copyright Copyright (c) 2023, WP Charitable LLC
8 8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 9 * @since 1.7.0.7
10 + * @version 1.8.9.1
10 11 */
11 12
12 13 // Exit if accessed directly.
13 14 if ( ! defined( 'ABSPATH' ) ) {
@@ -29,16 +30,16 @@
29 30 * @var Charitable_Import_Items|null
30 31 */
31 32 private static $instance = null;
32 33
33 - /**
34 - * Holds any plugin error messages.
35 - *
36 - * @since 1.0.0
37 - *
38 - * @var array
39 - */
40 - public $errors = array();
34 + /**
35 + * Holds any plugin error messages.
36 + *
37 + * @since 1.0.0
38 + *
39 + * @var array
40 + */
41 + public $errors = array();
41 42
42 43 /**
43 44 * Create class object.
44 45 *
@@ -76,222 +77,217 @@
76 77 if ( ! is_admin() || empty( $_POST ) ) {
77 78 return;
78 79 }
79 80
80 - if (! isset( $_POST['charitable_nonce'] ) || ! wp_verify_nonce( $_POST['charitable_nonce'], 'import_campaign' ) ) {
81 + if ( ! isset( $_POST['charitable_nonce'] ) || ! wp_verify_nonce( $_POST['charitable_nonce'], 'import_campaign' ) ) { // phpcs:ignore
81 82 return;
82 83 }
83 84
84 - if ( ! $this->has_json_extension() ) {
85 - $this->errors[] = __( 'Sorry, but Envira Gallery import files must be in <code>.json</code> format.', 'envira-gallery' );
86 - $redirect_link = admin_url( 'admin.php?page=charitable-settings&tab=tools' );
87 - wp_safe_redirect( $redirect_link );
88 - exit;
89 - }
85 + if ( ! current_user_can( 'manage_charitable_settings' ) ) {
86 + return;
87 + }
90 88
91 - // Retrieve the JSON contents of the file. If that fails, return an error.
92 - $contents = $this->get_file_contents();
93 - if ( ! $contents ) {
94 - $this->errors[] = __( 'Sorry, but there was an error retrieving the contents of the campaign export file. Please try again.', 'envira-gallery' );
95 - $redirect_link = admin_url( 'admin.php?page=charitable-settings&tab=tools' );
96 - wp_safe_redirect( $redirect_link );
97 - exit;
98 - }
89 + if ( ! $this->has_json_extension() ) {
90 + $this->errors[] = __( 'Sorry, but Charitable import files must be in <code>.json</code> format.', 'charitable' );
91 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=charitable' );
92 + wp_safe_redirect( $redirect_link );
93 + exit;
94 + }
99 95
100 - // Decode the settings and start processing.
101 - $data = json_decode( $contents, true );
96 + // Retrieve the JSON contents of the file. If that fails, return an error.
97 + $contents = $this->get_file_contents();
98 + if ( ! $contents ) {
99 + $this->errors[] = __( 'Sorry, but there was an error retrieving the contents of the campaign export file. Please try again.', 'charitable' );
100 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=charitable' );
101 + wp_safe_redirect( $redirect_link );
102 + exit;
103 + }
102 104
103 - // get the old post ID as we assign the new one, as we will want to document this as metadata.
104 - $old_post_id = $data['post']['ID'];
105 + // Decode the settings and start processing.
106 + $data = json_decode( $contents, true );
105 107
106 - // since we don't import authors/campaign creators, determine if the email address of the campaign creater exists in this site.
107 - $old_post_author_email = $data['campaign_creator']['user_email'];
108 - $campaign_creator = get_user_by('email', $old_post_author_email );
109 - if ( $campaign_creator ) {
110 - $campaign_creator_id = $campaign_creator->ID;
111 - } else {
112 - // assign it to the user who is logged in?
113 - $campaign_creator_id = get_current_user_id();
114 - }
108 + // get the old post ID as we assign the new one, as we will want to document this as metadata.
109 + $old_post_id = $data['post']['ID'];
115 110
116 - $campaign_data = array(
117 - 'post_title' => $data['post']['post_title'],
118 - 'post_content' => $data['post']['post_content'],
119 - 'post_excerpt' => $data['post']['post_excerpt'],
120 - 'post_date' => $data['post']['post_date'],
121 - 'post_date_gmt' => $data['post']['post_date_gmt'],
122 - 'comment_status' => $data['post']['comment_status'],
123 - 'ping_status' => $data['post']['ping_status'],
124 - 'post_name' => $data['post']['post_name'],
125 - 'post_modified' => $data['post']['post_modified'],
126 - 'post_modified_gmt' => $data['post']['post_modified_gmt'],
127 - 'post_content_filtered' => $data['post']['post_content_filtered'],
128 - 'guid' => $data['post']['guid'],
129 - 'post_mime_type' => $data['post']['post_mime_type'],
130 - 'comment_count' => $data['post']['comment_count'],
131 - 'post_status' => 'draft',
132 - 'post_type' => 'campaign',
133 - 'post_author' => $campaign_creator_id,
134 - );
135 - $campaign_id = wp_insert_post( $campaign_data );
111 + // since we don't import authors/campaign creators, determine if the email address of the campaign creater exists in this site.
112 + $old_post_author_email = $data['campaign_creator']['user_email'];
113 + $campaign_creator = get_user_by( 'email', $old_post_author_email );
114 + if ( $campaign_creator ) {
115 + $campaign_creator_id = $campaign_creator->ID;
116 + } else {
117 + // assign it to the user who is logged in?
118 + $campaign_creator_id = get_current_user_id();
119 + }
136 120
137 - foreach ( $data['meta'] as $meta_import_id => $meta_to_import ) {
138 - if ( is_serialized( $meta_to_import['meta_value'] ) ) {
139 - $data_to_import = unserialize( $meta_to_import['meta_value'] );
140 - } else {
141 - $data_to_import = $meta_to_import['meta_value'];
142 - }
143 - add_post_meta( $campaign_id, $meta_to_import['meta_key'], $data_to_import );
144 - }
121 + $campaign_data = array(
122 + 'post_title' => $data['post']['post_title'],
123 + 'post_content' => $data['post']['post_content'],
124 + 'post_excerpt' => $data['post']['post_excerpt'],
125 + 'post_date' => $data['post']['post_date'],
126 + 'post_date_gmt' => $data['post']['post_date_gmt'],
127 + 'comment_status' => $data['post']['comment_status'],
128 + 'ping_status' => $data['post']['ping_status'],
129 + 'post_name' => $data['post']['post_name'],
130 + 'post_modified' => $data['post']['post_modified'],
131 + 'post_modified_gmt' => $data['post']['post_modified_gmt'],
132 + 'post_content_filtered' => $data['post']['post_content_filtered'],
133 + 'guid' => $data['post']['guid'],
134 + 'post_mime_type' => $data['post']['post_mime_type'],
135 + 'comment_count' => $data['post']['comment_count'],
136 + 'post_status' => 'draft',
137 + 'post_type' => 'campaign',
138 + 'post_author' => $campaign_creator_id,
139 + );
140 + $campaign_id = wp_insert_post( $campaign_data );
145 141
146 - add_post_meta( $campaign_id,'_campaign_imported_campaign_key', intval( $old_post_id ) );
142 + foreach ( $data['meta'] as $meta_import_id => $meta_to_import ) {
143 + if ( is_serialized( $meta_to_import['meta_value'] ) ) {
144 + $data_to_import = unserialize( $meta_to_import['meta_value'], array( 'allowed_classes' => false ) );
145 + } else {
146 + $data_to_import = $meta_to_import['meta_value'];
147 + }
148 + add_post_meta( $campaign_id, $meta_to_import['meta_key'], $data_to_import );
149 + }
147 150
148 - // categories and tags
149 - if ( isset( $data['campaign_category'] ) && ! empty( $data['campaign_category'] ) ) {
150 - foreach ( $data['campaign_category'] as $term_to_import ) {
151 - $term = term_exists( $term_to_import['slug'], 'campaign_category' );
152 - if ( ! $term ) { // create category }
153 - $term_id = wp_insert_term(
154 - $term_to_import['name'], // the term
155 - 'campaign_category', // the taxonomy
156 - array(
157 - 'description' => $term_to_import['description'],
158 - 'slug' => $term_to_import['slug'],
159 - )
160 - );
161 - } else {
162 - $term_id = intval( $term['term_id'] );
163 - }
164 - wp_set_post_terms( $campaign_id, array( $term_id ), 'campaign_category', true );
151 + add_post_meta( $campaign_id, '_campaign_imported_campaign_key', intval( $old_post_id ) );
165 152
166 - }
167 - }
168 - if ( isset( $data['campaign_tag'] ) && ! empty( $data['campaign_tag'] ) ) {
169 - foreach ( $data['campaign_tag'] as $term_to_import ) {
170 - $term = term_exists( $term_to_import['slug'], 'campaign_tag' );
171 - if ( ! $term ) { // create category }
172 - $term_id = wp_insert_term(
173 - $term_to_import['name'], // the term
174 - 'campaign_tag', // the taxonomy
175 - array(
176 - 'description' => $term_to_import['description'],
177 - 'slug' => $term_to_import['slug'],
178 - )
179 - );
180 - } else {
181 - $term_id = intval( $term['term_id'] );
182 - }
183 - wp_set_post_terms( $campaign_id, array( $term_id ), 'campaign_tag', true );
153 + // categories and tags.
154 + if ( isset( $data['campaign_category'] ) && ! empty( $data['campaign_category'] ) ) {
155 + foreach ( $data['campaign_category'] as $term_to_import ) {
156 + $term = term_exists( $term_to_import['slug'], 'campaign_category' );
157 + if ( ! $term ) { // create category.
158 + $term_id = wp_insert_term(
159 + $term_to_import['name'], // the term.
160 + 'campaign_category', // the taxonomy.
161 + array(
162 + 'description' => $term_to_import['description'],
163 + 'slug' => $term_to_import['slug'],
164 + )
165 + );
166 + } else {
167 + $term_id = intval( $term['term_id'] );
168 + }
169 + wp_set_post_terms( $campaign_id, array( $term_id ), 'campaign_category', true );
184 170
185 - }
186 - }
171 + }
172 + }
173 + if ( isset( $data['campaign_tag'] ) && ! empty( $data['campaign_tag'] ) ) {
174 + foreach ( $data['campaign_tag'] as $term_to_import ) {
175 + $term = term_exists( $term_to_import['slug'], 'campaign_tag' );
176 + if ( ! $term ) { // create category.
177 + $term_id = wp_insert_term(
178 + $term_to_import['name'], // the term.
179 + 'campaign_tag', // the taxonomy.
180 + array(
181 + 'description' => $term_to_import['description'],
182 + 'slug' => $term_to_import['slug'],
183 + )
184 + );
185 + } else {
186 + $term_id = intval( $term['term_id'] );
187 + }
188 + wp_set_post_terms( $campaign_id, array( $term_id ), 'campaign_tag', true );
187 189
188 - // get featured image
189 - if ( isset( $data['thumbnail'] ) && ! empty( $data['thumbnail'] ) ) {
190 + }
191 + }
190 192
191 - // Add Featured Image to Post
192 - // $image_url = esc_url( $data['thumbnail'] ); // Define the image URL here
193 - // $image_name = basename( $data['thumbnail'] );
194 - // $upload_dir = wp_upload_dir(); // Set upload folder
195 - // $image_data = file_get_contents( $image_url ); // Get image data
196 - // $unique_file_name = wp_unique_filename( $upload_dir['path'], $image_name ); // Generate unique name
197 - // $filename = basename( $unique_file_name ); // Create image file name
193 + // get featured image.
194 + if ( isset( $data['thumbnail'] ) && ! empty( $data['thumbnail'] ) ) {
198 195
199 - // Prepare variables.
200 - $src = esc_url( $data['thumbnail'] );
201 - $stream = wp_remote_get( $src, array( 'timeout' => 60 ) );
202 - $type = wp_remote_retrieve_header( $stream, 'content-type' );
203 - $filename = basename( $src );
204 - $fileinfo = pathinfo( $filename );
196 + // Prepare variables.
197 + $src = esc_url( $data['thumbnail'] );
198 + $stream = wp_remote_get( $src, array( 'timeout' => 60 ) );
199 + $type = wp_remote_retrieve_header( $stream, 'content-type' );
200 + $filename = basename( $src );
201 + $fileinfo = pathinfo( $filename );
205 202
206 - // If the filename doesn't have an extension on it, determine the filename to use to save this image to the Media Library
207 - // This fixes importing URLs with no file extension e.g. http://placehold.it/300x300 (which is a PNG).
208 - if ( ! isset( $fileinfo['extension'] ) || empty( $fileinfo['extension'] ) ) {
209 - switch ( $type ) {
210 - case 'image/jpeg':
211 - $filename = $filename . '.jpeg';
212 - break;
213 - case 'image/jpg':
214 - $filename = $filename . '.jpg';
215 - break;
216 - case 'image/gif':
217 - $filename = $filename . '.gif';
218 - break;
219 - case 'image/png':
220 - $filename = $filename . '.png';
221 - break;
222 - case 'image/webp':
223 - $filename = $filename . '.webp';
224 - break;
225 - }
226 - }
203 + // If the filename doesn't have an extension on it, determine the filename to use to save this image to the Media Library
204 + // This fixes importing URLs with no file extension e.g. http://placehold.it/300x300 (which is a PNG).
205 + if ( ! isset( $fileinfo['extension'] ) || empty( $fileinfo['extension'] ) ) {
206 + switch ( $type ) {
207 + case 'image/jpeg':
208 + $filename = $filename . '.jpeg';
209 + break;
210 + case 'image/jpg':
211 + $filename = $filename . '.jpg';
212 + break;
213 + case 'image/gif':
214 + $filename = $filename . '.gif';
215 + break;
216 + case 'image/png':
217 + $filename = $filename . '.png';
218 + break;
219 + case 'image/webp':
220 + $filename = $filename . '.webp';
221 + break;
222 + }
223 + }
227 224
228 - // If we cannot get the image or determine the type, bail.
229 - if ( is_wp_error( $stream ) ) {
225 + // If we cannot get the image or determine the type, bail.
226 + if ( is_wp_error( $stream ) ) {
230 227
231 - if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
232 - error_log ('admin_accept_import_campaign_request:: ');
233 - error_log ( print_r( $stream, true ) );
234 - }
228 + if ( charitable_is_debug() ) {
229 + error_log( 'admin_accept_import_campaign_request:: ' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
230 + error_log( print_r( $stream, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
231 + }
235 232
236 - } elseif ( ! $type || strpos( $type, 'text/html' ) !== false ) {
233 + } elseif ( ! $type || strpos( $type, 'text/html' ) !== false ) {
237 234
238 - if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
239 - error_log ('admin_accept_import_campaign_request type: ');
240 - error_log ( print_r( $type, true ) );
241 - }
235 + if ( charitable_is_debug() ) {
236 + error_log( 'admin_accept_import_campaign_request type: ' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
237 + error_log( print_r( $type, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
238 + }
242 239
243 - } else {
244 - // It is an image. Stream the image.
245 - $mirror = wp_upload_bits( $filename, null, wp_remote_retrieve_body( $stream ) );
240 + } else {
241 + // It is an image. Stream the image.
242 + $mirror = wp_upload_bits( $filename, null, wp_remote_retrieve_body( $stream ) );
246 243
247 - // If there is an error, bail.
248 - if ( ! empty( $mirror['error'] ) ) {
244 + // If there is an error, bail.
245 + if ( ! empty( $mirror['error'] ) ) {
249 246
250 - if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
251 - error_log ('admin_accept_import_campaign_request:: ');
252 - error_log ( print_r( $stream, true ) );
253 - }
247 + if ( charitable_is_debug() ) {
248 + error_log( 'admin_accept_import_campaign_request:: ' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
249 + error_log( print_r( $stream, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
250 + }
254 251
255 - } else {
256 - // Check if the $item has title, caption, alt specified
257 - // If so, store those values against the attachment so they're included in the Gallery
258 - // If not, fallback to the defaults.
259 - $attachment = array(
260 - 'post_title' => ( ( isset( $data['post']['post_title'] ) && ! empty($data['post']['post_title'] ) ) ? $data['post']['post_title'] : urldecode( $filename ) ), // Title.
261 - 'post_mime_type' => $type,
262 - 'post_excerpt' => ( ( isset( $data['post']['post_excerpt'] ) && ! empty( $data['post']['post_excerpt'] ) ) ? $data['post']['post_excerpt'] : '' ), // Caption.
263 - );
264 - $attach_id = wp_insert_attachment( $attachment, $mirror['file'], $campaign_id );
265 - if ( ( isset( $item['alt'] ) && ! empty( $item['alt'] ) ) ) {
266 - update_post_meta( $attach_id, '_wp_attachment_image_alt', $item['alt'] );
267 - }
252 + } else {
253 + // Check if the $item has title, caption, alt specified
254 + // If so, store those values against the attachment so they're included in the Gallery
255 + // If not, fallback to the defaults.
256 + $attachment = array(
257 + 'post_title' => ( ( isset( $data['post']['post_title'] ) && ! empty( $data['post']['post_title'] ) ) ? $data['post']['post_title'] : urldecode( $filename ) ), // Title.
258 + 'post_mime_type' => $type,
259 + 'post_excerpt' => ( ( isset( $data['post']['post_excerpt'] ) && ! empty( $data['post']['post_excerpt'] ) ) ? $data['post']['post_excerpt'] : '' ), // Caption.
260 + );
261 + $attach_id = wp_insert_attachment( $attachment, $mirror['file'], $campaign_id );
262 + if ( ( isset( $item['alt'] ) && ! empty( $item['alt'] ) ) ) {
263 + update_post_meta( $attach_id, '_wp_attachment_image_alt', $item['alt'] );
264 + }
268 265
269 - // Generate and update attachment metadata.
270 - if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
271 - require ABSPATH . 'wp-admin/includes/image.php';
272 - }
266 + // Generate and update attachment metadata.
267 + if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
268 + require ABSPATH . 'wp-admin/includes/image.php';
269 + }
273 270
274 - // Generate and update attachment metadata.
275 - $attach_data = wp_generate_attachment_metadata( $attach_id, $mirror['file'] );
276 - wp_update_attachment_metadata( $attach_id, $attach_data );
271 + // Generate and update attachment metadata.
272 + $attach_data = wp_generate_attachment_metadata( $attach_id, $mirror['file'] );
273 + wp_update_attachment_metadata( $attach_id, $attach_data );
277 274
278 - // And finally assign featured image to post
279 - set_post_thumbnail( $campaign_id, $attach_id );
275 + // And finally assign featured image to post.
276 + set_post_thumbnail( $campaign_id, $attach_id );
280 277
281 - }
282 - }
278 + }
279 + }
283 280
284 - }
281 + }
285 282
286 - $this->add_update_message( __( 'Campaign imported.', 'charitable' ) . ' <a href="' . admin_url( 'post.php?post=' . $campaign_id . '&action=edit' ) . '">View campaign</a>.', 'success' );
283 + $this->add_update_message( __( 'Campaign imported.', 'charitable' ) . ' <a href="' . admin_url( 'post.php?post=' . $campaign_id . '&action=edit' ) . '">View campaign</a>.', 'success' );
287 284
288 - $redirect_link = admin_url( 'admin.php?page=charitable-settings&tab=tools' );
285 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=charitable' );
289 286 wp_safe_redirect( $redirect_link );
290 287 exit;
288 + }
291 289
292 - }
293 -
294 290 /**
295 291 * This forces the charitable menu to be open for category and tag pages.
296 292 *
297 293 * @since 1.7.0.7
@@ -305,259 +301,632 @@
305 301 if ( ! is_admin() || empty( $_POST ) ) {
306 302 return;
307 303 }
308 304
309 - if ( ! isset( $_POST['charitable_nonce'] ) || ! wp_verify_nonce( $_POST['charitable_nonce'], 'import_donations' ) ) {
305 + if ( ! isset( $_POST['charitable_nonce'] ) || ! wp_verify_nonce( $_POST['charitable_nonce'], 'import_donations' ) ) { // phpcs:ignore
310 306 return;
311 307 }
312 308
313 - if ( ! $this->has_json_extension('import_donations' ) ) {
314 - $this->add_update_message( __( 'Sorry, but Envira Gallery import files must be in <code>.json</code> format.', 'charitable' ), 'error' );
315 - $redirect_link = admin_url( 'admin.php?page=charitable-settings&tab=tools' );
316 - wp_safe_redirect( $redirect_link );
317 - exit;
318 - }
309 + if ( ! current_user_can( 'manage_charitable_settings' ) ) {
310 + return;
311 + }
319 312
320 - // Retrieve the JSON contents of the file. If that fails, return an error.
321 - $contents = $this->get_file_contents( 'import_donations' );
322 - if ( ! $contents ) {
323 - $this->add_update_message( __( 'Sorry, but there was an error retrieving the contents of the donation export file. Please try again.', 'charitable' ), 'error' );
324 - $redirect_link = admin_url( 'admin.php?page=charitable-settings&tab=tools' );
325 - wp_safe_redirect( $redirect_link );
326 - exit;
327 - }
313 + if ( ! $this->has_json_extension( 'import_donations' ) ) {
314 + $this->add_update_message( __( 'Sorry, but Charitable import files must be in <code>.json</code> format.', 'charitable' ), 'error' );
315 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=charitable' );
316 + wp_safe_redirect( $redirect_link );
317 + exit;
318 + }
328 319
329 - // Decode the settings and start processing.
330 - $data = json_decode( $contents, true );
320 + // Retrieve the JSON contents of the file. If that fails, return an error.
321 + $contents = $this->get_file_contents( 'import_donations' );
322 + if ( ! $contents ) {
323 + $this->add_update_message( __( 'Sorry, but there was an error retrieving the contents of the donation export file. Please try again.', 'charitable' ), 'error' );
324 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=charitable' );
325 + wp_safe_redirect( $redirect_link );
326 + exit;
327 + }
331 328
332 - // Get the campaign we are importing these donations into, otherwise bail.
333 - $campaign_id = isset( $_POST['charitable_settings']['tools_campaign'] ) ? intval( $_POST['charitable_settings']['tools_campaign'] ) : false;
329 + // Decode the settings and start processing.
330 + $data = json_decode( $contents, true );
334 331
335 - if ( false === $campaign_id || ! isset( $data['campaigns'] ) || count( $data['campaigns'] ) === 0 ) {
336 - $this->add_update_message( __( 'Sorry, but there was an error attempting to determine the campaign ID for this import. Please try again.', 'charitable' ), 'error' );
337 - $redirect_link = admin_url( 'admin.php?page=charitable-settings&tab=tools' );
338 - wp_safe_redirect( $redirect_link );
339 - exit;
340 - }
332 + // Get the campaign we are importing these donations into, otherwise bail.
333 + $campaign_id = isset( $_POST['charitable_settings']['tools_campaign'] ) ? intval( $_POST['charitable_settings']['tools_campaign'] ) : false;
341 334
342 - $campaign_name = get_the_title( $campaign_id ) ? get_the_title( $campaign_id ) : 'Unknown Campaign';
343 - $donations_added = 0;
335 + if ( false === $campaign_id || ! isset( $data['campaigns'] ) || count( $data['campaigns'] ) === 0 ) {
336 + $this->add_update_message( __( 'Sorry, but there was an error attempting to determine the campaign ID for this import. Please try again.', 'charitable' ), 'error' );
337 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=charitable' );
338 + wp_safe_redirect( $redirect_link );
339 + exit;
340 + }
344 341
345 - foreach ( $data['campaigns'] as $old_campaign_id => $campaign_donations_info ) {
346 - foreach ( $campaign_donations_info['donation_posts'] as $old_donation_id => $donation_data ) {
342 + $campaign_name = get_the_title( $campaign_id ) ? get_the_title( $campaign_id ) : 'Unknown Campaign';
343 + $donations_added = 0;
347 344
348 - $donor_data = false;
349 - $found_key = false;
350 - $donation_log = array();
345 + foreach ( $data['campaigns'] as $old_campaign_id => $campaign_donations_info ) {
346 + foreach ( $campaign_donations_info['donation_posts'] as $old_donation_id => $donation_data ) {
351 347
352 - if ( ! isset( $data['campaigns'][ $old_campaign_id ]['charitable_campaign_donations'] ) ) {
353 - continue;
354 - }
348 + $donor_data = false;
349 + $found_key = false;
350 + $donation_log = array();
355 351
356 - // confirm the existance of the extra table data, otherwise adding this donation is meaningless.
357 - foreach ( $campaign_donations_info['charitable_campaign_donations'] as $key => $charitable_campaign_donation ) {
358 - if ( intval( $charitable_campaign_donation['donation_id'] ) === $old_donation_id ) {
359 - $found_key = $key;
360 - }
361 - }
352 + if ( ! isset( $data['campaigns'][ $old_campaign_id ]['charitable_campaign_donations'] ) ) {
353 + continue;
354 + }
362 355
363 - if ( ! $found_key ) {
364 - continue;
365 - }
356 + // confirm the existance of the extra table data, otherwise adding this donation is meaningless.
357 + foreach ( $campaign_donations_info['charitable_campaign_donations'] as $key => $charitable_campaign_donation ) {
358 + if ( intval( $charitable_campaign_donation['donation_id'] ) === $old_donation_id ) {
359 + $found_key = $key;
360 + }
361 + }
366 362
367 - if ( ! isset( $donation_data['post'] ) ) {
368 - if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) {
369 - error_log ('import post fail donation data: ');
370 - error_log ( print_r( $donation_data, true ) );
371 - }
372 - return;
373 - }
363 + if ( ! $found_key ) {
364 + continue;
365 + }
374 366
375 - // create the donation post.
376 - $donation_post_args = array(
377 - 'post_title' => $donation_data['post']['post_title'],
378 - 'post_content' => $donation_data['post']['post_content'],
379 - 'post_excerpt' => $donation_data['post']['post_excerpt'],
380 - 'post_date' => $donation_data['post']['post_date'],
381 - 'post_date_gmt' => $donation_data['post']['post_date_gmt'],
382 - 'comment_status' => $donation_data['post']['comment_status'],
383 - 'ping_status' => $donation_data['post']['ping_status'],
384 - 'post_name' => $donation_data['post']['post_name'],
385 - 'post_modified' => $donation_data['post']['post_modified'],
386 - 'post_modified_gmt' => $donation_data['post']['post_modified_gmt'],
387 - 'post_content_filtered' => $donation_data['post']['post_content_filtered'],
388 - 'guid' => $donation_data['post']['guid'],
389 - 'post_mime_type' => $donation_data['post']['post_mime_type'],
390 - 'comment_count' => $donation_data['post']['comment_count'],
391 - 'post_status' => $donation_data['post']['post_status'], // 'charitable-pending',
392 - 'post_type' => 'donation',
393 - 'post_author' => $this->get_creator_id( $donation_data['author'] ) // $campaign_creator_id,
394 - );
395 - $donation_id = wp_insert_post( $donation_post_args );
367 + if ( ! isset( $donation_data['post'] ) ) {
368 + if ( charitable_is_debug() ) {
369 + error_log( 'import post fail donation data: ' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
370 + error_log( print_r( $donation_data, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log,WordPress.PHP.DevelopmentFunctions.error_log_print_r
371 + }
372 + return;
373 + }
396 374
397 - // import meta
398 - foreach ( $donation_data['meta'] as $meta_import_id => $meta_to_import ) {
399 - if ( is_serialized( $meta_to_import['meta_value'] ) ) {
400 - $data_to_import = unserialize( $meta_to_import['meta_value'] );
401 - } else {
402 - $data_to_import = $meta_to_import['meta_value'];
403 - }
404 - if ( $meta_to_import['meta_key'] === 'donor' ) {
405 - $donor_data = unserialize( $meta_to_import['meta_value'] );
406 - }
407 - if ( $meta_to_import['meta_key'] === '_donation_log' ) {
408 - $donation_log = unserialize( $meta_to_import['meta_value'] );
409 - }
375 + // create the donation post.
376 + $donation_post_args = array(
377 + 'post_title' => $donation_data['post']['post_title'],
378 + 'post_content' => $donation_data['post']['post_content'],
379 + 'post_excerpt' => $donation_data['post']['post_excerpt'],
380 + 'post_date' => $donation_data['post']['post_date'],
381 + 'post_date_gmt' => $donation_data['post']['post_date_gmt'],
382 + 'comment_status' => $donation_data['post']['comment_status'],
383 + 'ping_status' => $donation_data['post']['ping_status'],
384 + 'post_name' => $donation_data['post']['post_name'],
385 + 'post_modified' => $donation_data['post']['post_modified'],
386 + 'post_modified_gmt' => $donation_data['post']['post_modified_gmt'],
387 + 'post_content_filtered' => $donation_data['post']['post_content_filtered'],
388 + 'guid' => $donation_data['post']['guid'],
389 + 'post_mime_type' => $donation_data['post']['post_mime_type'],
390 + 'comment_count' => $donation_data['post']['comment_count'],
391 + 'post_status' => $donation_data['post']['post_status'], // 'charitable-pending',
392 + 'post_type' => 'donation',
393 + 'post_author' => $this->get_creator_id( $donation_data['author'] ), // $campaign_creator_id,
394 + );
395 + $donation_id = wp_insert_post( $donation_post_args );
410 396
411 - add_post_meta( $donation_id, $meta_to_import['meta_key'], $data_to_import );
412 - }
397 + // import meta.
398 + foreach ( $donation_data['meta'] as $meta_import_id => $meta_to_import ) {
399 + if ( is_serialized( $meta_to_import['meta_value'] ) ) {
400 + $data_to_import = unserialize( $meta_to_import['meta_value'], array( 'allowed_classes' => false ) ); // phpcs:ignore
401 + } else {
402 + $data_to_import = $meta_to_import['meta_value'];
403 + }
404 + if ( $meta_to_import['meta_key'] === 'donor' ) {
405 + $donor_data = unserialize( $meta_to_import['meta_value'], array( 'allowed_classes' => false ) ); // phpcs:ignore
406 + }
407 + if ( $meta_to_import['meta_key'] === '_donation_log' ) {
408 + $donation_log = unserialize( $meta_to_import['meta_value'], array( 'allowed_classes' => false ) ); // phpcs:ignore
409 + }
413 410
414 - add_post_meta( $donation_id,'_campaign_imported_donation_id', intval( $old_campaign_id ) );
411 + add_post_meta( $donation_id, $meta_to_import['meta_key'], $data_to_import );
412 + }
415 413
416 - // determine donor id from the donor data (email is key).
417 - $donor_id = $this->get_donor_id( $donor_data ); // $campaign_creator_id,
414 + add_post_meta( $donation_id, '_campaign_imported_donation_id', intval( $old_campaign_id ) );
418 415
419 - // update custom table.
420 - $table = $wpdb->prefix . 'charitable_campaign_donations';
421 - $args = array(
422 - 'donation_id' => $donation_id,
423 - 'donor_id' => $donor_id,
424 - 'campaign_id' => $campaign_id,
425 - 'campaign_name' => $campaign_name,
426 - 'amount' => $campaign_donations_info['charitable_campaign_donations'][ $found_key ]['amount'], // ... and so on
427 - );
416 + // determine donor id from the donor data (email is key).
417 + $donor_id = $this->get_donor_id( $donor_data ); // $campaign_creator_id,
428 418
429 - $wpdb->insert( $table, $args );
419 + // update custom table.
420 + $table = $wpdb->prefix . 'charitable_campaign_donations';
421 + $args = array(
422 + 'donation_id' => $donation_id,
423 + 'donor_id' => $donor_id,
424 + 'campaign_id' => $campaign_id,
425 + 'campaign_name' => $campaign_name,
426 + 'amount' => $campaign_donations_info['charitable_campaign_donations'][ $found_key ]['amount'], // ... and so on
427 + );
430 428
431 - // update log.
432 - $donation_log[] = array ( 'time' => time(), 'message' => 'Donation Imported.' );
433 - update_post_meta( $donation_id, '_donation_log', $donation_log );
429 + $wpdb->insert( $table, $args ); // phpcs:ignore
434 430
435 - $donations_added++;
431 + // update log.
432 + $donation_log[] = array(
433 + 'time' => time(),
434 + 'message' => 'Donation Imported.',
435 + );
436 + update_post_meta( $donation_id, '_donation_log', $donation_log );
436 437
437 - }
438 - }
438 + ++$donations_added;
439 439
440 - $this->add_update_message( __( $donations_added . ' donations imported.', 'charitable' ), 'success' );
440 + }
441 + }
441 442
442 - $redirect_link = admin_url( 'admin.php?page=charitable-settings&tab=tools&status=success+' . $donations_added );
443 + /* translators: %d is the number of donations imported. */
444 + $this->add_update_message( sprintf( __( '%d donations imported.', 'charitable' ), $donations_added ), 'success' );
445 +
446 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=charitable&status=success+' . $donations_added );
443 447 wp_safe_redirect( $redirect_link );
444 448 exit;
449 + }
445 450
446 - }
451 + /**
452 + * Determines if a gallery import file has a proper file extension.
453 + *
454 + * @since 1.0.0
455 + *
456 + * @param string $import_what The type of import file.
457 + *
458 + * @return bool True if the imported gallery file has a proper file extension, false otherwise.
459 + */
460 + public function has_json_extension( $import_what = 'import_campaign' ) {
447 461
448 - /**
449 - * Determines if a gallery import file has a proper file extension.
450 - *
451 - * @since 1.0.0
452 - *
453 - * @return bool True if the imported gallery file has a proper file extension, false otherwise.
454 - */
455 - public function has_json_extension( $import_what = 'import_campaign' ) {
462 + $file_array = isset( $_FILES['charitable_settings']['name']['import'][ $import_what ] ) ? explode( '.', $_FILES['charitable_settings']['name']['import'][ $import_what ] ) : null; // phpcs:ignore
463 + $extension = end( $file_array );
464 + return 'json' === $extension;
465 + }
456 466
457 - $file_array = isset( $_FILES['charitable_settings']['name']['tools'][$import_what] ) ? explode( '.', $_FILES['charitable_settings']['name']['tools'][$import_what] ) : null;
458 - $extension = end( $file_array );
459 - return 'json' === $extension;
467 + /**
468 + * Retrieve the contents of the imported gallery file.
469 + *
470 + * @since 1.0.0
471 + *
472 + * @param string $import_what The type of import file.
473 + *
474 + * @return string|bool JSON contents string if successful, false otherwise.
475 + */
476 + public function get_file_contents( $import_what = 'import_campaign' ) {
477 + $file = isset( $_FILES['charitable_settings']['tmp_name']['import'][ $import_what ] ) // phpcs:ignore
478 + ? wp_unslash( $_FILES['charitable_settings']['tmp_name']['import'][ $import_what ] ) // phpcs:ignore
479 + : false; // phpcs:ignore
460 480
461 - }
481 + if ( ! $file || ! file_exists( $file ) ) {
482 + return false;
483 + }
462 484
463 - /**
464 - * Retrieve the contents of the imported gallery file.
465 - *
466 - * @since 1.0.0
467 - *
468 - * @return string|bool JSON contents string if successful, false otherwise.
469 - */
470 - public function get_file_contents( $import_what = 'import_campaign' ) {
485 + $contents = file_get_contents( $file );
486 + if ( $contents === false ) {
487 + return false;
488 + }
471 489
472 - $file = isset( $_FILES['charitable_settings']['tmp_name']['tools'][$import_what] ) ? wp_unslash( $_FILES['charitable_settings']['tmp_name']['tools'][$import_what] ) : false;
473 - return file_get_contents( $file );
490 + return $contents;
491 + }
474 492
475 - }
493 + /**
494 + * Locate the user id of someone using this email
495 + *
496 + * @since 1.0.0
497 + *
498 + * @param array $author_data The author data.
499 + * @param boolean $zero_allowed Whether a zero value is allowed.
500 + *
501 + * @return string|bool JSON contents string if successful, false otherwise.
502 + */
503 + public function get_creator_id( $author_data, $zero_allowed = false ) {
476 504
477 - /**
478 - * Locate the user id of someone using this email
479 - *
480 - * @since 1.0.0
481 - *
482 - * @return string|bool JSON contents string if successful, false otherwise.
483 - */
484 - public function get_creator_id( $author_data, $zero_allowed = false ) {
505 + $email = false;
485 506
486 - $email = false;
507 + // try finding a user by email first.
508 + if ( isset( $author_data['email'] ) ) {
509 + $email = $author_data['email'];
510 + } elseif ( isset( $author_data['user_email'] ) ) {
511 + $email = $author_data['user_email'];
512 + }
487 513
488 - // try finding a user by email first
489 - if ( isset( $author_data['email'] ) ) {
490 - $email = $author_data['email'];
491 - } else if ( isset( $author_data['user_email'] ) ) {
492 - $email = $author_data['user_email'];
493 - }
514 + if ( $email ) {
515 + $user = get_user_by( 'email', $email );
516 + if ( $user ) {
517 + return $user->ID;
518 + }
519 + }
494 520
495 - if ( $email ) {
496 - $user = get_user_by( 'email', $email );
497 - if ( $user ) {
498 - return $user->ID;
499 - }
500 - }
521 + return $zero_allowed ? 0 : get_current_user_id();
522 + }
501 523
502 - return $zero_allowed ? 0 : get_current_user_id();
524 + /**
525 + * Locate the user id of someone using this email
526 + *
527 + * @since 1.0.0
528 + *
529 + * @param array $donor_data The donor data.
530 + *
531 + * @return string|bool JSON contents string if successful, false otherwise.
532 + */
533 + public function get_donor_id( $donor_data ) {
503 534
504 - }
535 + global $wpdb;
505 536
506 - /**
507 - * Locate the user id of someone using this email
508 - *
509 - * @since 1.0.0
510 - *
511 - * @return string|bool JSON contents string if successful, false otherwise.
512 - */
513 - public function get_donor_id( $donor_data ) {
537 + $email = ! empty( $donor_data['email'] ) ? $donor_data['email'] : '';
514 538
515 - global $wpdb;
539 + // update custom table.
540 + $table = $wpdb->prefix . 'charitable_donors';
516 541
517 - $email = esc_html( $donor_data['email'] );
542 + // try finding if they still exist first.
543 + $donor_id = $wpdb->get_var( $wpdb->prepare( "SELECT donor_id FROM {$table} WHERE email = %s", $email ) ); // phpcs:ignore
518 544
519 - // update custom table.
520 - $table = $wpdb->prefix . 'charitable_donors';
545 + // if not, create it in the table.
546 + if ( $donor_id === null ) {
521 547
522 - // try finding if they still exist first.
523 - $donor_id = $wpdb->get_var("SELECT donor_id FROM $table WHERE email = '$email'");
548 + $donor_id = $wpdb->insert( // phpcs:ignore
549 + $table,
550 + array(
551 + 'donor_id' => $donor_id,
552 + 'user_id' => $this->get_creator_id( $donor_data, true ),
553 + 'email' => ! empty( $donor_data['email'] ) ? $donor_data['email'] : '',
554 + 'first_name' => ! empty( $donor_data['first_name'] ) ? $donor_data['first_name'] : '',
555 + 'last_name' => ! empty( $donor_data['last_name'] ) ? $donor_data['last_name'] : '',
556 + 'date_joined' => ! empty( $donor_data['date_joined'] ) ? $donor_data['date_joined'] : '',
557 + )
558 + );
524 559
525 - // if not, create it in the table.
526 - if ( $donor_id == null) {
560 + }
527 561
528 - $donor_id = $wpdb->insert( $table, array(
529 - 'donor_id' => $donor_id,
530 - 'user_id' => $this->get_creator_id( $donor_data, true ),
531 - 'email' => $donor_data['email'],
532 - 'first_name' => $donor_data['first_name'],
533 - 'last_name' => $donor_data['last_name'],
534 - 'date_joined' => $donor_data['date_joined'],
535 - ));
562 + return $donor_id;
563 + }
536 564
537 - }
565 + /**
566 + * Process a GiveWP CSV donation upload.
567 + *
568 + * Maps GiveWP CSV columns to Charitable donation creation.
569 + *
570 + * @since 1.8.10
571 + *
572 + * @return void
573 + */
574 + public function admin_accept_import_donations_givewp_request() {
538 575
539 - return $donor_id;
540 - }
576 + if ( ! is_admin() || empty( $_POST ) ) {
577 + return;
578 + }
541 579
542 - /**
543 - * Add an update message.
544 - *
545 - * @since 1.4.6
546 - *
547 - * @param string $message The message text.
548 - * @param string $type The type of message. Options: 'error', 'success', 'warning', 'info'.
549 - * @param boolean $dismissible Whether the message can be dismissed.
550 - * @return void
551 - */
552 - public function add_update_message( $message, $type = 'error', $dismissible = true ) {
553 - if ( ! in_array( $type, array( 'error', 'success', 'warning', 'info' ) ) ) {
554 - $type = 'error';
555 - }
580 + if ( ! isset( $_POST['charitable_nonce'] ) || ! wp_verify_nonce( $_POST['charitable_nonce'], 'import_donations_givewp' ) ) { // phpcs:ignore
581 + return;
582 + }
556 583
557 - charitable_get_admin_notices()->add_notice( $message, $type, false, $dismissible );
558 - }
584 + if ( ! current_user_can( 'manage_charitable_settings' ) ) {
585 + return;
586 + }
559 587
588 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=givewp' );
560 589
561 -}
590 + if ( ! $this->has_correct_extension( 'import__givewp', 'import_donations_givewp_csv', 'csv' ) ) {
591 + $this->add_update_message( __( 'Sorry, but the import file must be in CSV format.', 'charitable' ), 'error' );
592 + wp_safe_redirect( $redirect_link );
593 + exit;
594 + }
595 +
596 + $file = $this->get_csv_file_path( 'import__givewp', 'import_donations_givewp_csv' );
597 + if ( ! $file ) {
598 + $this->add_update_message( __( 'Sorry, there was an error reading the uploaded file. Please try again.', 'charitable' ), 'error' );
599 + wp_safe_redirect( $redirect_link );
600 + exit;
601 + }
602 +
603 + $handle = fopen( $file, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
604 + if ( false === $handle ) {
605 + $this->add_update_message( __( 'Sorry, there was an error reading the uploaded file. Please try again.', 'charitable' ), 'error' );
606 + wp_safe_redirect( $redirect_link );
607 + exit;
608 + }
609 +
610 + $headers = fgetcsv( $handle );
611 + if ( ! $headers ) {
612 + fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
613 + $this->add_update_message( __( 'The CSV file appears to be empty or malformed.', 'charitable' ), 'error' );
614 + wp_safe_redirect( $redirect_link );
615 + exit;
616 + }
617 +
618 + // Normalize headers.
619 + $headers = array_map( 'trim', $headers );
620 +
621 + $donations_added = 0;
622 + $skipped = 0;
623 +
624 + while ( false !== ( $row = fgetcsv( $handle ) ) ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
625 + if ( count( $row ) < count( $headers ) ) {
626 + ++$skipped;
627 + continue;
628 + }
629 +
630 + $data = array_combine( $headers, $row );
631 +
632 + $first_name = isset( $data['First Name'] ) ? sanitize_text_field( $data['First Name'] ) : '';
633 + $last_name = isset( $data['Last Name'] ) ? sanitize_text_field( $data['Last Name'] ) : '';
634 + $email = isset( $data['Email Address'] ) ? sanitize_email( $data['Email Address'] ) : '';
635 + $amount = isset( $data['Donation Total'] ) ? floatval( $data['Donation Total'] ) : 0;
636 + $date = isset( $data['Donation Date'] ) ? sanitize_text_field( $data['Donation Date'] ) : '';
637 + $status = isset( $data['Donation Status'] ) ? sanitize_text_field( $data['Donation Status'] ) : '';
638 + $gateway = isset( $data['Payment Gateway'] ) ? sanitize_text_field( $data['Payment Gateway'] ) : '';
639 +
640 + if ( empty( $email ) || $amount <= 0 ) {
641 + ++$skipped;
642 + continue;
643 + }
644 +
645 + $donation_args = array(
646 + 'user' => array(
647 + 'email' => $email,
648 + 'first_name' => $first_name,
649 + 'last_name' => $last_name,
650 + ),
651 + 'campaigns' => array(
652 + array(
653 + 'campaign_id' => 0,
654 + 'amount' => $amount,
655 + ),
656 + ),
657 + 'gateway' => sanitize_text_field( $gateway ),
658 + 'status' => $this->map_givewp_status( $status ),
659 + 'log_note' => __( 'Imported from GiveWP CSV.', 'charitable' ),
660 + );
661 +
662 + if ( ! empty( $date ) ) {
663 + $donation_args['date'] = $date;
664 + }
665 +
666 + $donation_id = charitable_create_donation( $donation_args );
667 +
668 + if ( $donation_id ) {
669 + update_post_meta( $donation_id, '_givewp_csv_import', 1 );
670 + ++$donations_added;
671 + } else {
672 + ++$skipped;
673 + }
674 + }
675 +
676 + fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
677 +
678 + /* translators: %1$d: number of donations imported. %2$d: number of rows skipped. */
679 + $this->add_update_message( sprintf( __( '%1$d donations imported from GiveWP CSV. %2$d rows skipped.', 'charitable' ), $donations_added, $skipped ), 'success' );
680 +
681 + wp_safe_redirect( $redirect_link );
682 + exit;
683 + }
684 +
685 + /**
686 + * Process a GiveButter CSV donation upload.
687 + *
688 + * Maps GiveButter CSV columns to Charitable donation creation.
689 + *
690 + * @since 1.8.10
691 + *
692 + * @return void
693 + */
694 + public function admin_accept_import_donations_givebutter_request() {
695 +
696 + if ( ! is_admin() || empty( $_POST ) ) {
697 + return;
698 + }
699 +
700 + if ( ! isset( $_POST['charitable_nonce'] ) || ! wp_verify_nonce( $_POST['charitable_nonce'], 'import_donations_givebutter' ) ) { // phpcs:ignore
701 + return;
702 + }
703 +
704 + if ( ! current_user_can( 'manage_charitable_settings' ) ) {
705 + return;
706 + }
707 +
708 + $redirect_link = admin_url( 'admin.php?page=charitable-tools&tab=import&sub_tab=givebutter' );
709 +
710 + if ( ! $this->has_correct_extension( 'import__givebutter', 'import_donations_givebutter_csv', 'csv' ) ) {
711 + $this->add_update_message( __( 'Sorry, but the import file must be in CSV format.', 'charitable' ), 'error' );
712 + wp_safe_redirect( $redirect_link );
713 + exit;
714 + }
715 +
716 + $file = $this->get_csv_file_path( 'import__givebutter', 'import_donations_givebutter_csv' );
717 + if ( ! $file ) {
718 + $this->add_update_message( __( 'Sorry, there was an error reading the uploaded file. Please try again.', 'charitable' ), 'error' );
719 + wp_safe_redirect( $redirect_link );
720 + exit;
721 + }
722 +
723 + $handle = fopen( $file, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
724 + if ( false === $handle ) {
725 + $this->add_update_message( __( 'Sorry, there was an error reading the uploaded file. Please try again.', 'charitable' ), 'error' );
726 + wp_safe_redirect( $redirect_link );
727 + exit;
728 + }
729 +
730 + $headers = fgetcsv( $handle );
731 + if ( ! $headers ) {
732 + fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
733 + $this->add_update_message( __( 'The CSV file appears to be empty or malformed.', 'charitable' ), 'error' );
734 + wp_safe_redirect( $redirect_link );
735 + exit;
736 + }
737 +
738 + $headers = array_map( 'trim', $headers );
739 +
740 + $donations_added = 0;
741 + $skipped = 0;
742 +
743 + while ( false !== ( $row = fgetcsv( $handle ) ) ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
744 + if ( count( $row ) < count( $headers ) ) {
745 + ++$skipped;
746 + continue;
747 + }
748 +
749 + $data = array_combine( $headers, $row );
750 +
751 + $first_name = isset( $data['First Name'] ) ? sanitize_text_field( $data['First Name'] ) : '';
752 + $last_name = isset( $data['Last Name'] ) ? sanitize_text_field( $data['Last Name'] ) : '';
753 + $email = isset( $data['Email'] ) ? sanitize_email( $data['Email'] ) : '';
754 + $amount = isset( $data['Amount'] ) ? floatval( $data['Amount'] ) : 0;
755 + $date = isset( $data['Transaction Date (UTC)'] ) ? sanitize_text_field( $data['Transaction Date (UTC)'] ) : '';
756 + $status = isset( $data['Status Friendly'] ) ? sanitize_text_field( $data['Status Friendly'] ) : '';
757 + $method = isset( $data['Method'] ) ? sanitize_text_field( $data['Method'] ) : '';
758 + $message = isset( $data['Public Message'] ) ? sanitize_text_field( $data['Public Message'] ) : '';
759 +
760 + if ( empty( $email ) || $amount <= 0 ) {
761 + ++$skipped;
762 + continue;
763 + }
764 +
765 + $donation_args = array(
766 + 'user' => array(
767 + 'email' => $email,
768 + 'first_name' => $first_name,
769 + 'last_name' => $last_name,
770 + ),
771 + 'campaigns' => array(
772 + array(
773 + 'campaign_id' => 0,
774 + 'amount' => $amount,
775 + ),
776 + ),
777 + 'gateway' => sanitize_text_field( $method ),
778 + 'status' => $this->map_givebutter_status( $status ),
779 + 'log_note' => __( 'Imported from GiveButter CSV.', 'charitable' ),
780 + );
781 +
782 + if ( ! empty( $date ) ) {
783 + $donation_args['date'] = $date;
784 + }
785 +
786 + $donation_id = charitable_create_donation( $donation_args );
787 +
788 + if ( $donation_id ) {
789 + update_post_meta( $donation_id, '_givebutter_csv_import', 1 );
790 +
791 + // Add donor comment if there's a public message.
792 + if ( ! empty( $message ) ) {
793 + wp_insert_comment(
794 + array(
795 + 'comment_post_ID' => $donation_id,
796 + 'comment_content' => $message,
797 + 'comment_type' => 'charitable_comment',
798 + 'comment_approved' => 1,
799 + 'user_id' => 0,
800 + )
801 + );
802 + }
803 +
804 + ++$donations_added;
805 + } else {
806 + ++$skipped;
807 + }
808 + }
809 +
810 + fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
811 +
812 + /* translators: %1$d: number of donations imported. %2$d: number of rows skipped. */
813 + $this->add_update_message( sprintf( __( '%1$d donations imported from GiveButter CSV. %2$d rows skipped.', 'charitable' ), $donations_added, $skipped ), 'success' );
814 +
815 + wp_safe_redirect( $redirect_link );
816 + exit;
817 + }
818 +
819 + /**
820 + * Check if the uploaded file has the correct extension.
821 + *
822 + * Enhanced version supporting sub-tab file field lookups.
823 + *
824 + * @since 1.8.10
825 + *
826 + * @param string $section The section key (e.g. 'import__givewp').
827 + * @param string $field_name The field name within the section.
828 + * @param string $extension The expected file extension (e.g. 'csv').
829 + *
830 + * @return bool
831 + */
832 + public function has_correct_extension( $section, $field_name, $extension = 'csv' ) {
833 + $file_name = isset( $_FILES['charitable_settings']['name'][ $section ][ $field_name ] ) // phpcs:ignore
834 + ? $_FILES['charitable_settings']['name'][ $section ][ $field_name ] // phpcs:ignore
835 + : null;
836 +
837 + if ( empty( $file_name ) ) {
838 + return false;
839 + }
840 +
841 + $file_parts = explode( '.', $file_name );
842 + $file_ext = strtolower( end( $file_parts ) );
843 +
844 + return $extension === $file_ext;
845 + }
846 +
847 + /**
848 + * Get the temporary file path for a CSV upload.
849 + *
850 + * @since 1.8.10
851 + *
852 + * @param string $section The section key (e.g. 'import__givewp').
853 + * @param string $field_name The field name within the section.
854 + *
855 + * @return string|false The file path or false.
856 + */
857 + public function get_csv_file_path( $section, $field_name ) {
858 + $file = isset( $_FILES['charitable_settings']['tmp_name'][ $section ][ $field_name ] ) // phpcs:ignore
859 + ? wp_unslash( $_FILES['charitable_settings']['tmp_name'][ $section ][ $field_name ] ) // phpcs:ignore
860 + : false;
861 +
862 + if ( ! $file || ! file_exists( $file ) ) {
863 + return false;
864 + }
865 +
866 + return $file;
867 + }
868 +
869 + /**
870 + * Map a GiveWP donation status to a Charitable status.
871 + *
872 + * @since 1.8.10
873 + *
874 + * @param string $givewp_status The GiveWP status string.
875 + *
876 + * @return string The Charitable donation status.
877 + */
878 + public function map_givewp_status( $givewp_status ) {
879 + $map = array(
880 + 'Complete' => 'charitable-completed',
881 + 'Pending' => 'charitable-pending',
882 + 'Processing' => 'charitable-pending',
883 + 'Refunded' => 'charitable-refunded',
884 + 'Failed' => 'charitable-failed',
885 + 'Cancelled' => 'charitable-cancelled',
886 + 'Abandoned' => 'charitable-cancelled',
887 + 'Revoked' => 'charitable-cancelled',
888 + );
889 +
890 + return isset( $map[ $givewp_status ] ) ? $map[ $givewp_status ] : 'charitable-pending';
891 + }
892 +
893 + /**
894 + * Map a GiveButter donation status to a Charitable status.
895 + *
896 + * @since 1.8.10
897 + *
898 + * @param string $givebutter_status The GiveButter status string.
899 + *
900 + * @return string The Charitable donation status.
901 + */
902 + public function map_givebutter_status( $givebutter_status ) {
903 + $map = array(
904 + 'Succeeded' => 'charitable-completed',
905 + 'Pending' => 'charitable-pending',
906 + 'Failed' => 'charitable-failed',
907 + 'Refunded' => 'charitable-refunded',
908 + );
909 +
910 + return isset( $map[ $givebutter_status ] ) ? $map[ $givebutter_status ] : 'charitable-pending';
911 + }
912 +
913 + /**
914 + * Add an update message.
915 + *
916 + * @since 1.4.6
917 + *
918 + * @param string $message The message text.
919 + * @param string $type The type of message. Options: 'error', 'success', 'warning', 'info'.
920 + * @param boolean $dismissible Whether the message can be dismissed.
921 + * @return void
922 + */
923 + public function add_update_message( $message, $type = 'error', $dismissible = true ) {
924 + if ( ! in_array( $type, array( 'error', 'success', 'warning', 'info' ) ) ) {
925 + $type = 'error';
926 + }
927 +
928 + charitable_get_admin_notices()->add_notice( $message, $type, false, $dismissible );
929 + }
930 + }
562 931
563 932 endif;