PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / addons / csv / ajax / export.php

export.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at addons/csv/ajax/export.php

1,459 lines 46.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace StoreEngine\Addons\Csv\Ajax;
4
5 use StoreEngine\Addons\Subscription\Classes\SubscriptionCollection;
6 use StoreEngine\Classes\AbstractAjaxHandler;
7 use StoreEngine\Classes\AbstractProduct;
8 use StoreEngine\Classes\Customer;
9 use StoreEngine\Classes\EventStreamServer;
10 use StoreEngine\Classes\Exceptions\StoreEngineInvalidArgumentException;
11 use StoreEngine\Classes\Order\OrderItemCoupon;
12 use StoreEngine\Classes\Order\OrderItemProduct;
13 use StoreEngine\Classes\OrderCollection;
14 use StoreEngine\Classes\Product\VariableProduct;
15 use StoreEngine\Classes\StoreengineDatetime;
16 use StoreEngine\Utils\Formatting;
17 use StoreEngine\Utils\Helper;
18
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22
23 class Export extends AbstractAjaxHandler {
24
25 protected string $namespace = STOREENGINE_PLUGIN_SLUG . '_csv';
26
27 protected static EventStreamServer $sse;
28
29 public function __construct() {
30 // Vendors hold edit_storeengine_products too; results are scoped to
31 // their own data inside export_*() so they only see their own rows.
32 $this->actions = [
33 'file_download' => [
34 'callback' => [ $this, 'file_download' ],
35 'capability' => 'edit_storeengine_products',
36 'fields' => [
37 'filename' => 'string',
38 ],
39 ],
40 'export' => [
41 'callback' => [ $this, 'export' ],
42 'capability' => 'edit_storeengine_products',
43 'fields' => [
44 'type' => 'string',
45 ],
46 ],
47 ];
48 }
49
50 /**
51 * Returns the user_id to scope export queries by, or 0 for full visibility.
52 * Vendors get scoped to their own data; admin/shop_manager get everything.
53 */
54 protected function vendor_scope_user_id(): int {
55 if ( current_user_can( 'manage_options' )
56 || current_user_can( 'manage_storeengine_settings' )
57 || current_user_can( 'edit_others_storeengine_products' ) ) {
58 return 0;
59 }
60 if ( current_user_can( 'manage_storeengine_vendor' ) ) {
61 return (int) get_current_user_id();
62 }
63 return 0;
64 }
65
66 public function file_download( array $payload ) {
67 if ( ! isset( $payload['filename'] ) ) {
68 wp_send_json_error( __( 'Filename is required.', 'storeengine' ) );
69 }
70
71 $filename = sanitize_file_name( wp_basename( $payload['filename'] ) );
72 $upload_dir = trailingslashit( Helper::get_upload_dir() ) . 'csv/';
73 $filepath = $upload_dir . $filename;
74
75 $real_upload_dir = realpath( $upload_dir );
76 $real_filepath = realpath( $filepath );
77
78
79 if ( ! $real_filepath || strpos( $real_filepath, $real_upload_dir ) !== 0 ) {
80 wp_send_json_error( __( 'Invalid file path.', 'storeengine' ) );
81 }
82
83 if ( ! file_exists( $real_filepath ) ) {
84 wp_send_json_error( __( 'File not found.', 'storeengine' ) );
85 }
86
87 if ( ob_get_length() ) {
88 ob_end_clean();
89 }
90
91 header( 'Content-Type: application/octet-stream' );
92 header( 'Content-Disposition: attachment; filename="' . basename( $real_filepath ) . '"' );
93 header( 'Content-Length: ' . filesize( $real_filepath ) );
94
95 $handle = self::open_file( $real_filepath, 'rb' );
96 if ( $handle ) {
97 while ( ! feof( $handle ) ) {
98 echo fread( $handle, 8192 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions.file_system_operations_fread
99 flush();
100 }
101 self::close_file( $handle );
102 }
103 exit;
104 }
105
106 public function export( array $payload ) {
107 $valid = \StoreEngine\Addons\Csv\Utils\Import::validate_data_type( $payload['type'] );
108
109 if ( is_wp_error( $valid ) ) {
110 wp_send_json_error( $valid->get_error_message() );
111 }
112
113 // Vendors can only export data they own (currently products + orders).
114 // Customer / subscription / access-group exports remain admin-only.
115 if ( $this->vendor_scope_user_id() > 0
116 && ! in_array( (string) $payload['type'], [ 'products', 'orders' ], true ) ) {
117 wp_send_json_error( __( 'Vendors can only export their own products and orders.', 'storeengine' ) );
118 }
119
120 self::$sse = new EventStreamServer();
121 self::$sse->listen( function () use ( $payload ) {
122 /**
123 * @see export_products()
124 * @see export_orders()
125 * @see export_customers()
126 * @see export_access_groups()
127 * @see export_subscriptions()
128 */
129 $this->{'export_' . $payload['type']}();
130 } );
131 }
132
133 private function export_products() {
134 global $wpdb;
135 $scope_user_id = $this->vendor_scope_user_id();
136
137 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
138 if ( $scope_user_id > 0 ) {
139 $products_count = $wpdb->get_var( $wpdb->prepare(
140 "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = %s AND post_author = %d",
141 'storeengine_product',
142 $scope_user_id
143 ) );
144 } else {
145 $products_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = %s", 'storeengine_product' ) );
146 }
147 // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
148
149 $product_pages = ceil( $products_count / 20 );
150 $upload_dir = trailingslashit( Helper::get_upload_dir() . '/csv/' );
151 $current_time = time();
152 $filename = "products_{$products_count}_{$current_time}.csv";
153 $filepath = $upload_dir . $filename;
154
155 if ( ! is_dir( $upload_dir ) ) {
156 wp_mkdir_p( $upload_dir );
157 }
158
159 $fp = self::open_file( $filepath, 'w' );
160 if ( ! $fp ) {
161 self::$sse->emitEvent( [
162 'type' => 'error',
163 'message' => __( 'Unable to open file for writing.', 'storeengine' ),
164 ], true );
165 }
166 self::run_clean_hook( $filepath );
167
168 fputcsv( $fp, [
169 'ID',
170 'Type',
171 'Name',
172 'Slug',
173 'Published',
174 'Description',
175 'Is Hide',
176 'Shipping Type',
177 'Weight',
178 'Weight Unit',
179 'Product Categories',
180 'Product Tags',
181 'Images',
182 ] );
183
184 $index = 0;
185 foreach ( range( 1, $product_pages ) as $page ) {
186 $query_args = [
187 'posts_per_page' => 20,
188 'paged' => $page,
189 ];
190 if ( $scope_user_id > 0 ) {
191 $query_args['author'] = $scope_user_id;
192 }
193 $products = Helper::get_products( $query_args );
194 foreach ( $products as $product ) {
195 if ( $index > 0 ) {
196 fputcsv( $fp, [] );
197 fputcsv( $fp, [ '# Product Entity' ] );
198 }
199 $index ++;
200
201 fputcsv( $fp, [
202 esc_html( $product->get_id() ),
203 esc_html( $product->get_type() ),
204 esc_html( $product->get_name() ),
205 esc_html( $product->get_slug() ),
206 esc_html( $product->get_published_date_gmt() ),
207 wp_kses_post( $product->get_content() ),
208 absint( $product->is_hide() ),
209 esc_html( $product->get_shipping_type() ),
210 esc_html( $product->get_weight() ),
211 esc_html( $product->get_weight_unit() ),
212 esc_html( $this->get_product_categories( $product ) ),
213 esc_html( $this->get_product_tags( $product ) ),
214 esc_html( $this->get_product_images( $product ) ),
215 ] );
216
217 if ( ! empty( $product->get_prices() ) ) {
218 fputcsv( $fp, [] );
219 fputcsv( $fp, [
220 'Price name',
221 'Price type',
222 'Price',
223 'Compare price',
224 'Setup Fee',
225 'Setup Fee name',
226 'Setup Fee price',
227 'Setup Fee type',
228 'Trial',
229 'Trial days',
230 'Expire',
231 'Expire days',
232 'Payment duration',
233 'Payment duration type',
234 'Upgradeable',
235 'Order',
236 ] );
237 foreach ( $product->get_prices() as $price ) {
238 $price_data = $price->get_data();
239 unset( $price_data['order'] );
240 unset( $price_data['tax_status'] );
241 unset( $price_data['product_id'] );
242 $price_data = array_merge( array_values( $price_data ), [ $price->get_order() ] );
243 $price_data = array_map( 'esc_html', $price_data );
244
245 fputcsv( $fp, $price_data );
246 }
247 }
248
249 if ( ! empty( $product->get_downloadable_files() ) ) {
250 fputcsv( $fp, [] );
251 fputcsv( $fp, [
252 'Download ID',
253 'Download Name',
254 'Download URL',
255 'Download Status',
256 ] );
257 foreach ( $product->get_downloadable_files() as $file ) {
258 fputcsv( $fp, array_map( 'esc_html', array_values( $file ) ) );
259 }
260 }
261
262 if ( $product->is_type( 'variable' ) && $product instanceof VariableProduct ) {
263 fputcsv( $fp, [] );
264 fputcsv( $fp, [
265 'Variation Price',
266 'Variation Image',
267 'Variation Attributes',
268 'Price Index',
269 'Variation SKU',
270 'Variation Barcode',
271 'Variation Cost',
272 'Variation Stock',
273 ] );
274 $variations = array_map( function ( $variant ) use ( $product ) {
275 $attributes = [];
276 foreach ( $variant->get_attributes() as $attribute ) {
277 $taxonomy = get_taxonomy( $attribute->taxonomy );
278 $attributes[] = ( $taxonomy ? $taxonomy->labels->singular_name : str_replace( 'se_pa_', '', $attribute->taxonomy ) ) . ':' . $attribute->name;
279 }
280
281 return [
282 $variant->get_price(),
283 wp_get_attachment_image_url( $variant->get_featured_image() ),
284 implode( ',', $attributes ),
285 empty( $variant->get_pricing_id() ) ? null : $this->get_pricing_index( $product, $variant->get_pricing_id() ),
286 method_exists( $variant, 'get_sku' ) ? (string) $variant->get_sku() : '',
287 method_exists( $variant, 'get_barcode' ) ? (string) $variant->get_barcode() : '',
288 method_exists( $variant, 'get_cost_price' ) ? ( null === $variant->get_cost_price() ? '' : (string) $variant->get_cost_price() ) : '',
289 method_exists( $variant, 'get_stock_quantity' ) ? ( null === $variant->get_stock_quantity() ? '' : (string) $variant->get_stock_quantity() ) : '',
290 ];
291 }, $product->get_variants() );
292 foreach ( $variations as $variation ) {
293 fputcsv( $fp, array_map( 'esc_html', $variation ) );
294 }
295 }
296
297 self::$sse->emitEvent( [
298 'type' => 'progress',
299 'percentage' => round( ( $index / $products_count ) * 100 ),
300 ] );
301 }
302 }
303
304 self::close_file( $fp );
305
306 self::$sse->emitEvent( [
307 'type' => 'filename',
308 'message' => $filename,
309 ] );
310
311 self::$sse->emitEvent( [
312 'type' => 'complete',
313 'message' => esc_html__( 'Product export file has been ready.', 'storeengine' ),
314 ], true );
315 }
316
317 private function get_product_categories( AbstractProduct $product ): string {
318 $terms = wp_get_object_terms( $product->get_id(), 'storeengine_product_category', [ 'fields' => 'all' ] );
319
320 $paths = [];
321
322 foreach ( $terms as $term ) {
323 $path = [ $term->name ];
324 $parent_id = $term->parent;
325
326 while ( $parent_id ) {
327 $parent = get_term( $parent_id, 'storeengine_product_category' );
328 if ( is_wp_error( $parent ) || ! $parent ) {
329 break;
330 }
331 array_unshift( $path, $parent->name );
332 $parent_id = $parent->parent;
333 }
334
335 $paths[] = implode( ' > ', $path );
336 }
337
338 return implode( ', ', $paths );
339 }
340
341 private function get_product_tags( AbstractProduct $product ): string {
342 return implode( ', ', wp_get_object_terms( $product->get_id(), 'storeengine_product_tag', [ 'fields' => 'names' ] ) );
343 }
344
345 private function get_product_images( AbstractProduct $product ): string {
346 return implode( ', ',
347 array_unique(
348 array_filter(
349 array_merge(
350 [ get_the_post_thumbnail_url( $product->get_id() ) ],
351 array_map(
352 fn( $image_id ) => wp_get_attachment_image_url( $image_id ),
353 $product->get_product_gallery()
354 )
355 )
356 )
357 )
358 );
359 }
360
361 private function get_pricing_index( AbstractProduct $product, int $pricing_id ): ?int {
362 $pricing_index = 1;
363 foreach ( $product->get_prices() as $price ) {
364 if ( $price->get_id() === $pricing_id ) {
365 return $pricing_index;
366 }
367 $pricing_index ++;
368 }
369
370 return null;
371 }
372
373 private function export_orders() {
374 global $wpdb;
375 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
376 $orders_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}storeengine_orders WHERE type = %s", 'order' ) );
377 // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
378
379 $orders_pages = ceil( $orders_count / 20 );
380 $upload_dir = trailingslashit( Helper::get_upload_dir() . '/csv/' );
381 $current_time = time();
382 $filename = "orders_{$orders_count}_{$current_time}.csv";
383 $filepath = $upload_dir . $filename;
384
385 if ( ! is_dir( $upload_dir ) ) {
386 wp_mkdir_p( $upload_dir );
387 }
388
389 $fp = self::open_file( $filepath, 'w' );
390 if ( ! $fp ) {
391 self::$sse->emitEvent( [
392 'type' => 'error',
393 'message' => __( 'Unable to open file for writing.', 'storeengine' ),
394 ], true );
395 }
396 self::run_clean_hook( $filepath );
397
398 fputcsv( $fp, [
399 'Order Key',
400 'Order Status',
401 'Customer Email',
402 'Order Currency',
403 'Order Subtotal',
404 'Order Total',
405 'Order Tax',
406 'Date Created (GMT)',
407 'Date Updated (GMT)',
408 'Date Placed (GMT)',
409 'Date Paid (GMT)',
410 'Payment Method (name)',
411 'Payment Method (title)',
412 'Transaction ID',
413 'IP Address',
414 'User Agent',
415 'Customer Note',
416 'Cart Hash',
417 'Meta Data',
418 ] );
419
420 $index = 0;
421 foreach ( range( 1, $orders_pages ) as $page ) {
422 try {
423 $query = new OrderCollection( [
424 'per_page' => 20,
425 'paged' => $page,
426 'where' => [
427 'relation' => 'AND',
428 [
429 'key' => 'type',
430 'value' => 'order',
431 ],
432 [
433 'key' => 'status',
434 'value' => [ 'draft', 'auto-draft', 'trash' ],
435 'compare' => 'NOT IN',
436 ],
437 ],
438 ] );
439 if ( $query->have_results() ) {
440 foreach ( $query->get_results() as $order ) {
441 if ( $index > 0 ) {
442 fputcsv( $fp, [] );
443 fputcsv( $fp, [ '# Order Entity' ] );
444 }
445 $index ++;
446
447 $order_meta_data = [];
448 foreach ( $order->get_meta_data() as $meta ) {
449 $order_meta_data[] = $meta->key . ':' . $meta->value;
450 }
451 $user = get_userdata( $order->get_customer_id() );
452 fputcsv( $fp, [
453 esc_html( $order->get_order_key() ),
454 esc_html( $order->get_status() ),
455 esc_html( $user ? $user->user_email : $order->get_order_email() ),
456 esc_html( $order->get_currency() ),
457 esc_html( $order->get_subtotal() ),
458 esc_html( $order->get_total() ),
459 esc_html( $order->get_total_tax() ),
460 esc_html( $order->get_date_created_gmt()->format( 'Y-m-d h:i:s A' ) ),
461 esc_html( $order->get_date_updated_gmt() ? $order->get_date_updated_gmt()->format( 'Y-m-d h:i:s A' ) : '' ),
462 esc_html( $order->get_order_placed_date_gmt() ? $order->get_order_placed_date_gmt()->format( 'Y-m-d h:i:s A' ) : '' ),
463 esc_html( $order->get_date_paid_gmt() ? $order->get_date_paid_gmt()->format( 'Y-m-d h:i:s A' ) : '' ),
464 esc_html( $order->get_payment_method() ),
465 esc_html( $order->get_payment_method_title() ),
466 esc_html( $order->get_transaction_id() ),
467 esc_html( $order->get_ip_address() ),
468 esc_html( $order->get_user_agent() ),
469 esc_html( $order->get_customer_note() ),
470 esc_html( $order->get_cart_hash() ),
471 esc_html( implode( '|', $order_meta_data ) ),
472 ] );
473
474 if ( ! empty( $order->get_items() ) ) {
475 fputcsv( $fp, [] );
476 fputcsv( $fp, [
477 'Order Item Name',
478 'Product Slug',
479 'Product Type',
480 'Price',
481 'Quantity',
482 'Subtotal',
483 'Subtotal (Tax)',
484 'Total',
485 'Total (Tax)',
486 'Product Shipping Type',
487 'Is Autocomplete',
488 'Price name',
489 'Price type',
490 'Setup Fee',
491 'Setup Fee name',
492 'Setup Fee price',
493 'Setup Fee type',
494 'Trial',
495 'Trial days',
496 'Expire',
497 'Expire days',
498 'Payment duration',
499 'Payment duration type',
500 'Meta Data',
501 ] );
502 /** @var OrderItemProduct $item */
503 foreach ( $order->get_items() as $item ) {
504 $metadata = [];
505 foreach ( $item->get_formatted_metadata() as $meta ) {
506 $metadata[] = "{$meta['key']}:{$meta['value']}";
507 }
508 fputcsv( $fp, [
509 esc_html( $item->get_name() ),
510 esc_html( $item->get_product() ? $item->get_product()->get_slug() : '' ),
511 esc_html( $item->get_product_type() ),
512 esc_html( $item->get_price() ),
513 absint( $item->get_quantity() ),
514 esc_html( $item->get_subtotal() ),
515 esc_html( $item->get_subtotal_tax() ),
516 esc_html( $item->get_total() ),
517 esc_html( $item->get_total_tax() ),
518 esc_html( $item->get_shipping_type() ),
519 esc_html( $item->get_digital_auto_complete() ),
520 esc_html( $item->get_price_name() ),
521 esc_html( $item->get_price_type() ),
522 esc_html( $item->get_setup_fee() ),
523 esc_html( $item->get_setup_fee_name() ),
524 esc_html( $item->get_setup_fee_price() ),
525 esc_html( $item->get_setup_fee_type() ),
526 esc_html( $item->get_trial() ),
527 esc_html( $item->get_trial_days() ),
528 esc_html( $item->get_expire() ),
529 esc_html( $item->get_expire_days() ),
530 esc_html( $item->get_payment_duration() ),
531 esc_html( $item->get_payment_duration_type() ),
532 esc_html( implode( '|', $metadata ) ),
533 ] );
534 }
535 }
536
537 if ( ! empty( $order->get_billing_country() ) ) {
538 fputcsv( $fp, [] );
539 fputcsv( $fp, [
540 'Billing First Name',
541 'Billing Last Name',
542 'Billing Company',
543 'Billing Address 1',
544 'Billing Address 2',
545 'Billing City',
546 'Billing State',
547 'Billing Postcode',
548 'Billing Country',
549 'Billing Email',
550 'Billing Phone',
551 ] );
552 fputcsv( $fp, [
553 esc_html( $order->get_billing_first_name() ),
554 esc_html( $order->get_billing_last_name() ),
555 esc_html( $order->get_billing_company() ),
556 esc_html( $order->get_billing_address_1() ),
557 esc_html( $order->get_billing_address_2() ),
558 esc_html( $order->get_billing_city() ),
559 esc_html( $order->get_billing_state() ),
560 esc_html( $order->get_billing_postcode() ),
561 esc_html( $order->get_billing_country() ),
562 esc_html( $order->get_billing_email() ),
563 esc_html( $order->get_billing_phone() ),
564 ] );
565 }
566
567 if ( ! empty( $order->get_shipping_country() ) ) {
568 fputcsv( $fp, [] );
569 fputcsv( $fp, [
570 'Shipping First Name',
571 'Shipping Last Name',
572 'Shipping Company',
573 'Shipping Address 1',
574 'Shipping Address 2',
575 'Shipping City',
576 'Shipping State',
577 'Shipping Postcode',
578 'Shipping Country',
579 'Shipping Email',
580 'Shipping Phone',
581 ] );
582 fputcsv( $fp, [
583 esc_html( $order->get_shipping_first_name() ),
584 esc_html( $order->get_shipping_last_name() ),
585 esc_html( $order->get_shipping_company() ),
586 esc_html( $order->get_shipping_address_1() ),
587 esc_html( $order->get_shipping_address_2() ),
588 esc_html( $order->get_shipping_city() ),
589 esc_html( $order->get_shipping_state() ),
590 esc_html( $order->get_shipping_postcode() ),
591 esc_html( $order->get_shipping_country() ),
592 esc_html( $order->get_shipping_email() ),
593 esc_html( $order->get_shipping_phone() ),
594 ] );
595 }
596
597 if ( ! empty( $order->get_coupons() ) ) {
598 fputcsv( $fp, [] );
599 fputcsv( $fp, [
600 'Coupon Code',
601 'Coupon Amount',
602 'Coupon Amount (Tax)',
603 ] );
604 /** @var OrderItemCoupon $coupon */
605 foreach ( $order->get_coupons() as $coupon ) {
606 fputcsv( $fp, [
607 esc_html( $coupon->get_code() ),
608 esc_html( $coupon->get_discount() ),
609 esc_html( $coupon->get_discount_tax() ),
610 ] );
611 }
612 }
613
614 if ( ! empty( $order->get_taxes() ) ) {
615 fputcsv( $fp, [] );
616 fputcsv( $fp, [
617 'Tax Name',
618 'Tax Label',
619 'Tax Amount',
620 'Tax Rate(%)',
621 'Shipping Tax Amount',
622 ] );
623
624 foreach ( $order->get_taxes() as $tax ) {
625 fputcsv( $fp, [
626 esc_html( $tax->get_name() ),
627 esc_html( $tax->get_label() ),
628 esc_html( $tax->get_tax_total() ),
629 esc_html( $tax->get_rate_percent() ),
630 esc_html( $tax->get_shipping_tax_total() ),
631 ] );
632 }
633 }
634
635 if ( ! empty( $order->get_shipping_methods() ) ) {
636 fputcsv( $fp, [] );
637 fputcsv( $fp, [
638 'Shipping Method',
639 'Shipping Method Title',
640 'Amount',
641 'Amount (Tax)',
642 'Tax Status',
643 ] );
644 foreach ( $order->get_shipping_methods() as $shipping_method ) {
645 fputcsv( $fp, [
646 esc_html( $shipping_method->get_method_id() ),
647 esc_html( $shipping_method->get_method_title() ),
648 esc_html( $shipping_method->get_total() ),
649 esc_html( $shipping_method->get_total_tax() ),
650 esc_html( $shipping_method->get_tax_status() ),
651 ] );
652 }
653 }
654
655 if ( ! empty( $order->get_fees() ) ) {
656 fputcsv( $fp, [] );
657 fputcsv( $fp, [
658 'Fee Name',
659 'Fee Amount',
660 'Fee Amount (Tax)',
661 'Tax Status',
662 ] );
663 foreach ( $order->get_fees() as $fee ) {
664 fputcsv( $fp, [
665 esc_html( $fee->get_name() ),
666 esc_html( $fee->get_amount() ),
667 esc_html( $fee->get_total_tax() ),
668 esc_html( $fee->get_tax_status() ),
669 ] );
670 }
671 }
672
673 if ( ! empty( $order->get_downloadable_permissions() ) ) {
674 fputcsv( $fp, [] );
675 fputcsv( $fp, [
676 'Download ID',
677 'Product Slug',
678 'Date Access Granted (GMT)',
679 ] );
680 foreach ( $order->get_downloadable_permissions() as $downloadable_item ) {
681 $product_post = get_post( $downloadable_item->get_product_id() );
682 if ( ! $product_post ) {
683 continue;
684 }
685 fputcsv( $fp, [
686 esc_html( $downloadable_item->get_download_id() ),
687 esc_html( $product_post->post_name ),
688 esc_html( $downloadable_item->get_access_granted_date() instanceof StoreengineDatetime ? $downloadable_item->get_access_granted_date()->format( 'Y-m-d h:i:s A' ) : $downloadable_item->get_access_granted_date() ),
689 ] );
690 }
691 }
692
693 if ( ! empty( $order->get_refunds() ) ) {
694 fputcsv( $fp, [] );
695 fputcsv( $fp, [
696 'Refund Amount',
697 'Refund Reason',
698 'Refund By',
699 ] );
700 foreach ( $order->get_refunds() as $refund ) {
701 $user = get_userdata( $refund->get_refunded_by() );
702 fputcsv( $fp, [
703 esc_html( $refund->get_amount() ),
704 esc_html( $refund->get_reason() ),
705 esc_html( $user ? $user->user_email : '' ),
706 ] );
707 }
708 }
709
710 self::$sse->emitEvent( [
711 'type' => 'progress',
712 'percentage' => round( ( $index / $orders_count ) * 100 ),
713 ] );
714 }
715 }
716 } catch ( StoreEngineInvalidArgumentException $e ) {
717 self::close_file( $fp );
718 self::$sse->emitEvent( [
719 'type' => 'error',
720 'message' => $e->getMessage(),
721 ], true );
722 }
723 }
724
725 self::close_file( $fp );
726
727 self::$sse->emitEvent( [
728 'type' => 'filename',
729 'message' => $filename,
730 ] );
731
732 self::$sse->emitEvent( [
733 'type' => 'complete',
734 'message' => esc_html__( 'Orders export file has been ready.', 'storeengine' ),
735 ], true );
736 }
737
738 private function export_customers() {
739 global $wpdb;
740 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
741 $customers_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users" );
742 // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
743
744 $customer_pages = ceil( $customers_count / 20 );
745 $upload_dir = trailingslashit( Helper::get_upload_dir() . '/csv/' );
746 $current_time = time();
747 $filename = "customers_{$customers_count}_$current_time.csv";
748 $filepath = $upload_dir . $filename;
749
750 if ( ! is_dir( $upload_dir ) ) {
751 wp_mkdir_p( $upload_dir );
752 }
753
754 $fp = self::open_file( $filepath, 'w' );
755 if ( ! $fp ) {
756 self::$sse->emitEvent( [
757 'type' => 'error',
758 'message' => __( 'Unable to open file for writing.', 'storeengine' ),
759 ], true );
760 }
761 self::run_clean_hook( $filepath );
762
763 fputcsv( $fp, [
764 'Username',
765 'First Name',
766 'Last Name',
767 'Nick Name',
768 'Display Name',
769 'Email',
770 'Website',
771 'Biographical Info',
772 'Registered Date (GMT)',
773 'Roles',
774 'Billing First Name',
775 'Billing Last Name',
776 'Billing Company',
777 'Billing Address 1',
778 'Billing Address 2',
779 'Billing City',
780 'Billing State',
781 'Billing Postcode',
782 'Billing Country',
783 'Billing Email',
784 'Billing Phone',
785 'Shipping First Name',
786 'Shipping Last Name',
787 'Shipping Company',
788 'Shipping Address 1',
789 'Shipping Address 2',
790 'Shipping City',
791 'Shipping State',
792 'Shipping Postcode',
793 'Shipping Country',
794 'Shipping Email',
795 'Shipping Phone',
796 'Email Subscribe',
797 ] );
798
799 $index = 0;
800 foreach ( range( 1, $customer_pages ) as $page ) {
801 /** @var Customer[] $customers */
802 $customers = Helper::get_customers( [
803 'number' => 20,
804 'paged' => $page,
805 ] );
806 foreach ( $customers as $customer ) {
807 fputcsv( $fp, [
808 esc_html( $customer->get_username() ),
809 esc_html( $customer->get_first_name() ),
810 esc_html( $customer->get_last_name() ),
811 esc_html( $customer->get_wp_user()->user_nicename ),
812 esc_html( $customer->get_display_name() ),
813 esc_html( $customer->get_email() ),
814 esc_html( $customer->get_url() ),
815 esc_html( $customer->get_wp_user()->user_description ),
816 esc_html( $customer->get_user_registered() ? $customer->get_user_registered()->format( 'Y-m-d h:i:s A' ) : '' ),
817 esc_html( implode( ',', $customer->get_wp_user()->roles ) ),
818 esc_html( $customer->get_billing_first_name() ),
819 esc_html( $customer->get_billing_last_name() ),
820 esc_html( $customer->get_billing_company() ),
821 esc_html( $customer->get_billing_address_1() ),
822 esc_html( $customer->get_billing_address_2() ),
823 esc_html( $customer->get_billing_city() ),
824 esc_html( $customer->get_billing_state() ),
825 esc_html( $customer->get_billing_postcode() ),
826 esc_html( $customer->get_billing_country() ),
827 esc_html( $customer->get_billing_email() ),
828 esc_html( $customer->get_billing_phone() ),
829 esc_html( $customer->get_shipping_first_name() ),
830 esc_html( $customer->get_shipping_last_name() ),
831 esc_html( $customer->get_shipping_company() ),
832 esc_html( $customer->get_shipping_address_1() ),
833 esc_html( $customer->get_shipping_address_2() ),
834 esc_html( $customer->get_shipping_city() ),
835 esc_html( $customer->get_shipping_state() ),
836 esc_html( $customer->get_shipping_postcode() ),
837 esc_html( $customer->get_shipping_country() ),
838 esc_html( $customer->get_shipping_email() ),
839 esc_html( $customer->get_shipping_phone() ),
840 Formatting::bool_to_string( $customer->get_subscribe_to_email() ),
841 ] );
842
843 self::$sse->emitEvent( [
844 'type' => 'progress',
845 'percentage' => round( ( $index / $customers_count ) * 100 ),
846 ] );
847 $index ++;
848 }
849 }
850
851 self::close_file( $fp );
852
853 self::$sse->emitEvent( [
854 'type' => 'filename',
855 'message' => $filename,
856 ] );
857
858 self::$sse->emitEvent( [
859 'type' => 'complete',
860 'message' => esc_html__( 'Customer export file has been ready.', 'storeengine' ),
861 ], true );
862 }
863
864 private function export_access_groups() {
865 $access_groups_count = wp_count_posts( 'storeengine_groups' )->publish;
866
867 $access_groups_pages = ceil( $access_groups_count / 20 );
868 $upload_dir = trailingslashit( Helper::get_upload_dir() . '/csv/' );
869 $current_time = time();
870 $filename = "access_groups_{$access_groups_count}_$current_time.csv";
871 $filepath = $upload_dir . $filename;
872
873 if ( ! is_dir( $upload_dir ) ) {
874 wp_mkdir_p( $upload_dir );
875 }
876
877 $fp = self::open_file( $filepath, 'w' );
878 if ( ! $fp ) {
879 self::$sse->emitEvent( [
880 'type' => 'error',
881 'message' => __( 'Unable to open file for writing.', 'storeengine' ),
882 ], true );
883 }
884 self::run_clean_hook( $filepath );
885
886 fputcsv( $fp, [
887 'Access Group Name',
888 'Unauthorized Access Message',
889 'Protected Content Type',
890 'Specific Content Items',
891 'Excluded Content Items',
892 'Downloadable files',
893 'Enable Redirect URL',
894 'Redirect URL',
895 'Enable Access Group Expiration',
896 'Expiration Date type',
897 'Expiration Relative Date',
898 'Expiration Specific Date',
899 'User Role Sync',
900 'Priority',
901 ] );
902
903 $rules = [
904 'basic-global' => 'Entire Website',
905 'post|all' => 'All Posts',
906 'post|all|taxarchive|category' => 'All Categories Archive',
907 'post|all|taxarchive|post_tag' => 'All Tags Archive',
908 'page|all' => 'All Pages',
909 'specifics' => 'Specific Posts/ Pages/ Taxonomies',
910 ];
911
912 $index = 0;
913 foreach ( range( 1, $access_groups_pages ) as $page ) {
914 $access_group_posts = get_posts( [
915 'post_type' => 'storeengine_groups',
916 'numberposts' => 20,
917 'offset' => max( ( $page - 1 ) * 20, 0 ),
918 ] );
919 foreach ( $access_group_posts as $access_group_post ) {
920 if ( $index > 0 ) {
921 fputcsv( $fp, [] );
922 fputcsv( $fp, [ '# Access Group Entity' ] );
923 }
924 $index ++;
925
926 $authorization_meta = get_post_meta( $access_group_post->ID, '_storeengine_membership_authorization', true );
927 $content_protect_data = get_post_meta( $access_group_post->ID, '_storeengine_membership_content_protect_types', true );
928 $content_excluded_items = get_post_meta( $access_group_post->ID, '_storeengine_membership_content_protect_excluded_items', true );
929 $attachments = get_post_meta( $access_group_post->ID, '_storeengine_membership_attachments', true );
930 $expiration_meta = get_post_meta( $access_group_post->ID, '_storeengine_membership_expiration', true );
931 $user_roles = get_post_meta( $access_group_post->ID, '_storeengine_membership_user_roles', true );
932
933 $content_types = [];
934 if ( ! empty( $content_protect_data ) && ! empty( $content_protect_data['rules'] ) ) {
935 $content_types = array_map( fn( $type ) => $rules[ $type ], $content_protect_data['rules'] );
936 }
937 $specifics = [];
938 if ( ! empty( $content_protect_data ) && ! empty( $content_protect_data['specifics'] ) ) {
939 foreach ( $content_protect_data['specifics'] as $specific ) {
940 $slug = $this->get_slug_from_item( $specific['value'] );
941 if ( $slug ) {
942 $specifics[] = $slug;
943 }
944 }
945 }
946
947 $excluded_items = [];
948 if ( ! empty( $content_excluded_items ) ) {
949 foreach ( $content_excluded_items as $excluded_item ) {
950 $slug = $this->get_slug_from_item( $excluded_item['value'] );
951 if ( $slug ) {
952 $excluded_items[] = $slug;
953 }
954 }
955 }
956
957 if ( is_array( $attachments ) ) {
958 $attachments = array_map( fn( $attachment_id ) => wp_get_attachment_image_url( $attachment_id, 'full' ), $attachments );
959 }
960
961 if ( is_array( $user_roles ) ) {
962 $user_roles = array_map( fn( $user_role ) => $user_role['value'], $user_roles );
963 }
964
965 fputcsv( $fp, [
966 esc_html( $access_group_post->post_title ),
967 esc_html( $authorization_meta['message'] ),
968 esc_html( implode( ',', $content_types ) ),
969 esc_html( implode( ',', $specifics ) ),
970 esc_html( implode( ',', $excluded_items ) ),
971 esc_html( implode( ',', $attachments ) ),
972 'redirect' === $authorization_meta['type'],
973 esc_html( $authorization_meta['redirect_url'] ),
974 Formatting::string_to_bool( $expiration_meta['is_enable_expiration'] ),
975 esc_html( $expiration_meta['date_type'] ?? '' ),
976 esc_html( $expiration_meta['fixed_date_duration'] ?? '' ),
977 esc_html( $expiration_meta['specific_date'] ?? '' ),
978 esc_html( implode( ',', $user_roles ) ),
979 esc_html( get_post_meta( $access_group_post->ID, '_storeengine_membership_priority', true ) ),
980 ] );
981
982 $integrations = Helper::get_integration_repository_by_id( 'storeengine/membership-addon', $access_group_post->ID );
983 if ( ! empty( $integrations ) ) {
984 fputcsv( $fp, [] );
985 fputcsv( $fp, [
986 'Product name',
987 'Product slug',
988 'Price name',
989 'Price type',
990 'Price',
991 'Compare price',
992 'Setup Fee',
993 'Setup Fee name',
994 'Setup Fee price',
995 'Setup Fee type',
996 'Trial',
997 'Trial days',
998 'Expire',
999 'Expire days',
1000 'Payment duration',
1001 'Payment duration type',
1002 'Upgradeable',
1003 'Order',
1004 ] );
1005 foreach ( $integrations as $integration ) {
1006 $price = $integration->price;
1007 $price_data = $price->get_data();
1008 unset( $price_data['order'] );
1009 unset( $price_data['tax_status'] );
1010 unset( $price_data['product_id'] );
1011 $product = $price->get_product();
1012
1013 $data = array_merge( [
1014 $product ? $product->get_name() : '',
1015 $product ? $product->get_slug() : '',
1016 ], array_values( $price_data ), [ $price->get_order() ] );
1017 $data = array_map( 'esc_html', $data );
1018 fputcsv( $fp, $data );
1019 }
1020 }
1021
1022 $features = get_post_meta( $access_group_post->ID, '_storeengine_membership_features', true );
1023 if ( ! empty( $features ) ) {
1024 fputcsv( $fp, [] );
1025 fputcsv( $fp, [
1026 'Feature Icon',
1027 'Feature Label',
1028 ] );
1029 foreach ( $features as $feature ) {
1030 fputcsv( $fp, [
1031 esc_html( $feature['icon'] ),
1032 esc_html( $feature['label'] ),
1033 ] );
1034 }
1035 }
1036
1037 self::$sse->emitEvent( [
1038 'type' => 'progress',
1039 'percentage' => round( ( $index / $access_groups_count ) * 100 ),
1040 ] );
1041 }
1042 }
1043
1044 self::close_file( $fp );
1045
1046 self::$sse->emitEvent( [
1047 'type' => 'filename',
1048 'message' => $filename,
1049 ] );
1050
1051 self::$sse->emitEvent( [
1052 'type' => 'complete',
1053 'message' => esc_html__( 'Access groups export file has been ready.', 'storeengine' ),
1054 ], true );
1055 }
1056
1057 private function get_slug_from_item( string $title ): ?string {
1058 if ( str_starts_with( $title, 'post-' ) ) {
1059 preg_match( '/post-(\d+)-\|/', $title, $matches );
1060 $post_id = $matches[1] ?? null;
1061 if ( ! $post_id ) {
1062 return null;
1063 }
1064 $post = get_post( $post_id );
1065 if ( ! $post ) {
1066 return null;
1067 }
1068
1069 return $post->post_name;
1070 }
1071
1072 if ( str_starts_with( $title, 'tax-' ) ) {
1073 $data = explode( '--single-', $title );
1074 if ( count( $data ) !== 2 ) {
1075 return null;
1076 }
1077 [ $term_id ] = $data;
1078 $term_id = (int) substr( $term_id, 4 );
1079 if ( ! $term_id ) {
1080 return null;
1081 }
1082 $term = get_term( $term_id );
1083 if ( ! $term ) {
1084 return null;
1085 }
1086
1087 return "$term->taxonomy::$term->slug";
1088 }
1089
1090 return null;
1091 }
1092
1093 private function export_subscriptions() {
1094 global $wpdb;
1095
1096 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
1097 $subscriptions_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}storeengine_orders WHERE type = %s", 'subscription' ) );
1098 // phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
1099
1100 $subscriptions_pages = ceil( $subscriptions_count / 20 );
1101 $upload_dir = trailingslashit( Helper::get_upload_dir() . '/csv/' );
1102 $current_time = time();
1103 $filename = "subscriptions_{$subscriptions_count}_{$current_time}.csv";
1104 $filepath = $upload_dir . $filename;
1105
1106 if ( ! is_dir( $upload_dir ) ) {
1107 wp_mkdir_p( $upload_dir );
1108 }
1109
1110 $fp = self::open_file( $filepath, 'w' );
1111 if ( ! $fp ) {
1112 self::$sse->emitEvent( [
1113 'type' => 'error',
1114 'message' => __( 'Unable to open file for writing.', 'storeengine' ),
1115 ], true );
1116 }
1117 self::run_clean_hook( $filepath );
1118
1119 fputcsv( $fp, [
1120 'Subscription Key',
1121 'Subscription Status',
1122 'Customer Email',
1123 'Subscription Currency',
1124 'Subscription Subtotal',
1125 'Subscription Total',
1126 'Subscription Tax',
1127 'Date Created (GMT)',
1128 'Date Updated (GMT)',
1129 'Date Placed (GMT)',
1130 'Date Paid (GMT)',
1131 'Payment Method (name)',
1132 'Payment Method (title)',
1133 'IP Address',
1134 'User Agent',
1135 'Customer Note',
1136 'Cart Hash',
1137 'Parent Order Key',
1138 'Child Order Keys',
1139 'Start Date (GMT)',
1140 'Has Trial',
1141 'Trial Days',
1142 'Trial End Date (GMT)',
1143 'Next Payment Date (GMT)',
1144 'Meta Data',
1145 ] );
1146
1147 $index = 0;
1148 foreach ( range( 1, $subscriptions_pages ) as $page ) {
1149 try {
1150 $query = new SubscriptionCollection( [
1151 'per_page' => 20,
1152 'paged' => $page,
1153 'where' => [
1154 [
1155 'key' => 'status',
1156 'value' => [ 'draft', 'auto-draft', 'trash' ],
1157 'compare' => 'NOT IN',
1158 ],
1159 ],
1160 ] );
1161 foreach ( $query->get_results() as $subscription ) {
1162 if ( $index > 0 ) {
1163 fputcsv( $fp, [] );
1164 fputcsv( $fp, [ '# Subscription Entity' ] );
1165 }
1166 $index ++;
1167
1168 $subscription_meta_data = [];
1169 foreach ( $subscription->get_meta_data() as $meta ) {
1170 $subscription_meta_data[] = $meta->key . ':' . $meta->value;
1171 }
1172 $user = get_userdata( $subscription->get_customer_id() );
1173 $parent_order = Helper::get_order( $subscription->get_parent_order_id() );
1174 $parent_id = $subscription->get_parent_order_id();
1175
1176 $related_orders = array_filter( $subscription->get_related_orders(), fn( $order_id ) => $order_id != $parent_id );
1177 $formatter = implode( ',', array_fill( 0, count( $related_orders ), '%d' ) );
1178 global $wpdb;
1179 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- query prepared.
1180 $child_order_keys = $wpdb->get_results( $wpdb->prepare( "SELECT order_key FROM {$wpdb->prefix}storeengine_order_operational_data WHERE order_id in ($formatter)", ...$related_orders ) );
1181 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- query prepared.
1182 $child_order_keys = array_map( fn( $obj ) => $obj->order_key, $child_order_keys );
1183
1184 fputcsv( $fp, [
1185 esc_html( $subscription->get_order_key() ),
1186 esc_html( $subscription->get_status() ),
1187 esc_html( $user ? $user->user_email : $subscription->get_order_email() ),
1188 esc_html( $subscription->get_currency() ),
1189 esc_html( $subscription->get_subtotal() ),
1190 esc_html( $subscription->get_total() ),
1191 esc_html( $subscription->get_total_tax() ),
1192 esc_html( $subscription->get_date_created_gmt()->format( 'Y-m-d h:i:s A' ) ),
1193 esc_html( $subscription->get_date_updated_gmt() ? $subscription->get_date_updated_gmt()->format( 'Y-m-d h:i:s A' ) : '' ),
1194 esc_html( $subscription->get_order_placed_date_gmt() ? $subscription->get_order_placed_date_gmt()->format( 'Y-m-d h:i:s A' ) : '' ),
1195 esc_html( $subscription->get_date_paid_gmt() ? $subscription->get_date_paid_gmt()->format( 'Y-m-d h:i:s A' ) : '' ),
1196 esc_html( $subscription->get_payment_method() ),
1197 esc_html( $subscription->get_payment_method_title() ),
1198 esc_html( $subscription->get_ip_address() ),
1199 esc_html( $subscription->get_user_agent() ),
1200 esc_html( $subscription->get_customer_note() ),
1201 esc_html( $subscription->get_cart_hash() ),
1202 esc_html( $parent_order && ! is_wp_error( $parent_order ) ? $parent_order->get_order_key() : '' ),
1203 esc_html( implode( ',', $child_order_keys ) ),
1204 esc_html( $subscription->get_start_date() ? $subscription->get_start_date()->format( 'Y-m-d h:i:s A' ) : '' ),
1205 esc_html( $subscription->get_trial() ),
1206 esc_html( $subscription->get_trial_days() ),
1207 esc_html( $subscription->get_trial_end_date() ? $subscription->get_trial_end_date()->format( 'Y-m-d h:i:s A' ) : '' ),
1208 esc_html( $subscription->get_next_payment_date() ? $subscription->get_next_payment_date()->format( 'Y-m-d h:i:s A' ) : '' ),
1209 esc_html( implode( '|', $subscription_meta_data ) ),
1210 ] );
1211
1212 if ( ! empty( $subscription->get_items() ) ) {
1213 fputcsv( $fp, [] );
1214 fputcsv( $fp, [
1215 'Item Name',
1216 'Product Slug',
1217 'Product Type',
1218 'Price',
1219 'Quantity',
1220 'Subtotal',
1221 'Subtotal (Tax)',
1222 'Total',
1223 'Total (Tax)',
1224 'Product Shipping Type',
1225 'Is Autocomplete',
1226 'Price name',
1227 'Price type',
1228 'Setup Fee',
1229 'Setup Fee name',
1230 'Setup Fee price',
1231 'Setup Fee type',
1232 'Trial',
1233 'Trial days',
1234 'Expire',
1235 'Expire days',
1236 'Payment duration',
1237 'Payment duration type',
1238 'Meta Data',
1239 ] );
1240 /** @var OrderItemProduct $item */
1241 foreach ( $subscription->get_items() as $item ) {
1242 $metadata = [];
1243 foreach ( $item->get_formatted_metadata() as $meta ) {
1244 $metadata[] = "{$meta['key']}:{$meta['value']}";
1245 }
1246 fputcsv( $fp, [
1247 esc_html( $item->get_name() ),
1248 esc_html( $item->get_product() ? $item->get_product()->get_slug() : '' ),
1249 esc_html( $item->get_product_type() ),
1250 esc_html( $item->get_price() ),
1251 esc_html( $item->get_quantity() ),
1252 esc_html( $item->get_subtotal() ),
1253 esc_html( $item->get_subtotal_tax() ),
1254 esc_html( $item->get_total() ),
1255 esc_html( $item->get_total_tax() ),
1256 esc_html( $item->get_shipping_type() ),
1257 $item->get_digital_auto_complete(),
1258 esc_html( $item->get_price_name() ),
1259 esc_html( $item->get_price_type() ),
1260 esc_html( $item->get_setup_fee() ),
1261 esc_html( $item->get_setup_fee_name() ),
1262 esc_html( $item->get_setup_fee_price() ),
1263 esc_html( $item->get_setup_fee_type() ),
1264 esc_html( $item->get_trial() ),
1265 esc_html( $item->get_trial_days() ),
1266 esc_html( $item->get_expire() ),
1267 esc_html( $item->get_expire_days() ),
1268 esc_html( $item->get_payment_duration() ),
1269 esc_html( $item->get_payment_duration_type() ),
1270 esc_html( implode( '|', $metadata ) ),
1271 ] );
1272 }
1273 }
1274
1275 if ( ! empty( $subscription->get_billing_country() ) ) {
1276 fputcsv( $fp, [] );
1277 fputcsv( $fp, [
1278 'Billing First Name',
1279 'Billing Last Name',
1280 'Billing Company',
1281 'Billing Address 1',
1282 'Billing Address 2',
1283 'Billing City',
1284 'Billing State',
1285 'Billing Postcode',
1286 'Billing Country',
1287 'Billing Email',
1288 'Billing Phone',
1289 ] );
1290 fputcsv( $fp, [
1291 esc_html( $subscription->get_billing_first_name() ),
1292 esc_html( $subscription->get_billing_last_name() ),
1293 esc_html( $subscription->get_billing_company() ),
1294 esc_html( $subscription->get_billing_address_1() ),
1295 esc_html( $subscription->get_billing_address_2() ),
1296 esc_html( $subscription->get_billing_city() ),
1297 esc_html( $subscription->get_billing_state() ),
1298 esc_html( $subscription->get_billing_postcode() ),
1299 esc_html( $subscription->get_billing_country() ),
1300 esc_html( $subscription->get_billing_email() ),
1301 esc_html( $subscription->get_billing_phone() ),
1302 ] );
1303 }
1304
1305 if ( ! empty( $subscription->get_shipping_country() ) ) {
1306 fputcsv( $fp, [] );
1307 fputcsv( $fp, [
1308 'Shipping First Name',
1309 'Shipping Last Name',
1310 'Shipping Company',
1311 'Shipping Address 1',
1312 'Shipping Address 2',
1313 'Shipping City',
1314 'Shipping State',
1315 'Shipping Postcode',
1316 'Shipping Country',
1317 'Shipping Email',
1318 'Shipping Phone',
1319 ] );
1320 fputcsv( $fp, [
1321 esc_html( $subscription->get_shipping_first_name() ),
1322 esc_html( $subscription->get_shipping_last_name() ),
1323 esc_html( $subscription->get_shipping_company() ),
1324 esc_html( $subscription->get_shipping_address_1() ),
1325 esc_html( $subscription->get_shipping_address_2() ),
1326 esc_html( $subscription->get_shipping_city() ),
1327 esc_html( $subscription->get_shipping_state() ),
1328 esc_html( $subscription->get_shipping_postcode() ),
1329 esc_html( $subscription->get_shipping_country() ),
1330 esc_html( $subscription->get_shipping_email() ),
1331 esc_html( $subscription->get_shipping_phone() ),
1332 ] );
1333 }
1334
1335 if ( ! empty( $subscription->get_coupons() ) ) {
1336 fputcsv( $fp, [] );
1337 fputcsv( $fp, [
1338 'Coupon Code',
1339 'Coupon Amount',
1340 'Coupon Amount (Tax)',
1341 ] );
1342 /** @var OrderItemCoupon $coupon */
1343 foreach ( $subscription->get_coupons() as $coupon ) {
1344 fputcsv( $fp, [
1345 esc_html( $coupon->get_code() ),
1346 esc_html( $coupon->get_discount() ),
1347 esc_html( $coupon->get_discount_tax() ),
1348 ] );
1349 }
1350 }
1351
1352 if ( ! empty( $subscription->get_taxes() ) ) {
1353 fputcsv( $fp, [] );
1354 fputcsv( $fp, [
1355 'Tax Name',
1356 'Tax Label',
1357 'Tax Amount',
1358 'Tax Rate(%)',
1359 'Shipping Tax Amount',
1360 ] );
1361
1362 foreach ( $subscription->get_taxes() as $tax ) {
1363 fputcsv( $fp, [
1364 esc_html( $tax->get_name() ),
1365 esc_html( $tax->get_label() ),
1366 esc_html( $tax->get_tax_total() ),
1367 esc_html( $tax->get_rate_percent() ),
1368 esc_html( $tax->get_shipping_tax_total() ),
1369 ] );
1370 }
1371 }
1372
1373 if ( ! empty( $subscription->get_shipping_methods() ) ) {
1374 fputcsv( $fp, [] );
1375 fputcsv( $fp, [
1376 'Shipping Method',
1377 'Shipping Method Title',
1378 'Amount',
1379 'Amount (Tax)',
1380 'Tax Status',
1381 ] );
1382 foreach ( $subscription->get_shipping_methods() as $shipping_method ) {
1383 fputcsv( $fp, [
1384 esc_html( $shipping_method->get_method_id() ),
1385 esc_html( $shipping_method->get_method_title() ),
1386 esc_html( $shipping_method->get_total() ),
1387 esc_html( $shipping_method->get_total_tax() ),
1388 esc_html( $shipping_method->get_tax_status() ),
1389 ] );
1390 }
1391 }
1392
1393 if ( ! empty( $subscription->get_fees() ) ) {
1394 fputcsv( $fp, [] );
1395 fputcsv( $fp, [
1396 'Fee Name',
1397 'Fee Amount',
1398 'Fee Amount (Tax)',
1399 'Tax Status',
1400 ] );
1401 foreach ( $subscription->get_fees() as $fee ) {
1402 fputcsv( $fp, [
1403 esc_html( $fee->get_name() ),
1404 esc_html( $fee->get_amount() ),
1405 esc_html( $fee->get_total_tax() ),
1406 esc_html( $fee->get_tax_status() ),
1407 ] );
1408 }
1409 }
1410
1411 self::$sse->emitEvent( [
1412 'type' => 'progress',
1413 'percentage' => round( ( $index / $subscriptions_count ) * 100 ),
1414 ] );
1415 }
1416 } catch ( StoreEngineInvalidArgumentException $e ) {
1417 self::close_file( $fp );
1418 self::$sse->emitEvent( [
1419 'type' => 'error',
1420 'message' => $e->getMessage(),
1421 ], true );
1422 }
1423 }
1424
1425 self::close_file( $fp );
1426
1427 self::$sse->emitEvent( [
1428 'type' => 'filename',
1429 'message' => $filename,
1430 ] );
1431
1432 self::$sse->emitEvent( [
1433 'type' => 'complete',
1434 'message' => esc_html__( 'Orders export file has been ready.', 'storeengine' ),
1435 ], true );
1436 }
1437
1438 public static function run_clean_hook( string $filepath ) {
1439 $hook = 'storeengine/csv/clean_tmp_csv';
1440 $args = [ $filepath ];
1441
1442 // Skip if a cleanup is already scheduled for this exact file.
1443 if ( function_exists( 'as_has_scheduled_action' ) && as_has_scheduled_action( $hook, $args ) ) {
1444 return;
1445 }
1446
1447 as_schedule_single_action( time() + 3600, $hook, $args );
1448 }
1449
1450 private static function open_file( string $filename, string $mode, bool $use_include_path = false, $context = null ) {
1451 // @TODO use FS utils.
1452 return fopen( $filename, $mode, $use_include_path, $context ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
1453 }
1454
1455 private static function close_file( $stream ): bool {
1456 return fclose( $stream ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
1457 }
1458 }
1459