PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / includes / Controllers / ImportController.php

ImportController.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.0, at includes/Controllers/ImportController.php

190 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Import screen.
4 *
5 * @package Easy_Invoice
6 * @subpackage Controllers
7 */
8
9 namespace EasyInvoice\Controllers;
10
11 use EasyInvoice\Import\CsvImporter;
12 use EasyInvoice\Import\SlicedImporter;
13 use EasyInvoice\Import\SproutImporter;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * One screen: what is on this site that could be imported, a dry-run count
21 * for each source, and a button that does it. CSV uploads go through the
22 * same screen with template files to download.
23 */
24 class ImportController {
25
26 const PAGE_SLUG = 'easy-invoice-import';
27 const ACTION_RUN = 'easy_invoice_import_run';
28 const ACTION_TPL = 'easy_invoice_import_template';
29
30 public static function init(): void {
31 add_action( 'admin_menu', [ __CLASS__, 'registerPage' ], 99 );
32 add_action( 'easy_invoice_admin_main_content', [ __CLASS__, 'maybeRenderPage' ], 11 );
33 add_action( 'admin_post_' . self::ACTION_RUN, [ __CLASS__, 'handleRun' ] );
34 add_action( 'admin_post_' . self::ACTION_TPL, [ __CLASS__, 'handleTemplate' ] );
35 add_filter( 'easy_invoice_self_rendering_pages', [ __CLASS__, 'claimPage' ] );
36 }
37
38 public static function claimPage( $slugs ): array {
39 $slugs = is_array( $slugs ) ? $slugs : [];
40 $slugs[] = self::PAGE_SLUG;
41 return $slugs;
42 }
43
44 public static function registerPage(): void {
45 add_submenu_page( 'easy-invoice-hidden', __( 'Import', 'easy-invoice' ), __( 'Import', 'easy-invoice' ), 'manage_options', self::PAGE_SLUG, [ __CLASS__, 'renderShell' ] );
46 }
47
48 public static function renderShell(): void {
49 include EASY_INVOICE_PLUGIN_DIR . 'templates/main-template.php';
50 }
51
52 /**
53 * The importers that could run here, keyed by source.
54 *
55 * @return array<string,\EasyInvoice\Import\Importer>
56 */
57 public static function sources(): array {
58 $sources = [
59 'sliced' => new SlicedImporter(),
60 'sprout' => new SproutImporter(),
61 ];
62 /**
63 * Filter the available import sources.
64 *
65 * @param array $sources Importer instances keyed by source name.
66 */
67 return (array) apply_filters( 'easy_invoice_import_sources', $sources );
68 }
69
70 public static function maybeRenderPage( $page ): void {
71 if ( self::PAGE_SLUG !== $page || ! current_user_can( 'manage_options' ) ) {
72 return;
73 }
74 $sources = self::sources();
75 $previews = [];
76 foreach ( $sources as $key => $importer ) {
77 if ( $importer->available() ) {
78 $previews[ $key ] = $importer->preview();
79 }
80 }
81 $result = get_transient( 'easy_invoice_import_result_' . get_current_user_id() );
82 delete_transient( 'easy_invoice_import_result_' . get_current_user_id() );
83
84 include EASY_INVOICE_PLUGIN_DIR . 'templates/admin/import-page.php';
85 }
86
87 /**
88 * Run an import.
89 *
90 * @return void
91 */
92 public static function handleRun(): void {
93 check_admin_referer( self::ACTION_RUN );
94 if ( ! current_user_can( 'manage_options' ) ) {
95 wp_die( esc_html__( 'You do not have permission to import.', 'easy-invoice' ), '', [ 'response' => 403 ] );
96 }
97
98 $source = isset( $_POST['source'] ) ? sanitize_key( $_POST['source'] ) : '';
99
100 // Imports of any size take a while; never let the request die halfway.
101 if ( function_exists( 'set_time_limit' ) ) {
102 @set_time_limit( 0 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
103 }
104 wp_raise_memory_limit( 'admin' );
105
106 if ( 'csv' === $source ) {
107 $importer = new CsvImporter( self::uploaded( 'clients_csv' ), self::uploaded( 'invoices_csv' ) );
108 if ( ! $importer->preview()['clients'] && ! $importer->preview()['invoices'] ) {
109 self::finish( [ 'error' => __( 'Choose at least one CSV file with a header row.', 'easy-invoice' ) ] );
110 }
111 } else {
112 $sources = self::sources();
113 if ( ! isset( $sources[ $source ] ) ) {
114 self::finish( [ 'error' => __( 'Unknown import source.', 'easy-invoice' ) ] );
115 }
116 $importer = $sources[ $source ];
117 }
118
119 $report = $importer->run();
120 $report['label'] = $importer->label();
121
122 /**
123 * Fires after an import has run.
124 *
125 * @param string $source Source key.
126 * @param array $report Counts and notes.
127 */
128 do_action( 'easy_invoice_import_finished', $source, $report );
129
130 self::finish( $report );
131 }
132
133 /**
134 * Serve a CSV template.
135 *
136 * @return void
137 */
138 public static function handleTemplate(): void {
139 check_admin_referer( self::ACTION_TPL );
140 if ( ! current_user_can( 'manage_options' ) ) {
141 wp_die( '', '', [ 'response' => 403 ] );
142 }
143 $which = ( isset( $_GET['which'] ) && 'clients' === $_GET['which'] ) ? 'clients' : 'invoices';
144 nocache_headers();
145 header( 'Content-Type: text/csv; charset=utf-8' );
146 header( 'Content-Disposition: attachment; filename="easy-invoice-' . $which . '-template.csv"' );
147 echo CsvImporter::template( $which ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV download.
148 exit;
149 }
150
151 /**
152 * Path of an uploaded CSV, moved into the uploads dir; '' when none.
153 *
154 * @param string $field Form field.
155 * @return string
156 */
157 private static function uploaded( string $field ): string {
158 if ( empty( $_FILES[ $field ]['tmp_name'] ) || UPLOAD_ERR_OK !== (int) ( $_FILES[ $field ]['error'] ?? 1 ) ) {
159 return '';
160 }
161 if ( ! function_exists( 'wp_handle_upload' ) ) {
162 require_once ABSPATH . 'wp-admin/includes/file.php';
163 }
164 $moved = wp_handle_upload( $_FILES[ $field ], [ // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- validated by wp_handle_upload.
165 'test_form' => false,
166 'mimes' => [ 'csv' => 'text/csv', 'txt' => 'text/plain' ],
167 ] );
168 if ( ! is_array( $moved ) || ! empty( $moved['error'] ) || empty( $moved['file'] ) ) {
169 return '';
170 }
171 // Delete after the request: the file has no reason to stay in the media folder.
172 register_shutdown_function( static function () use ( $moved ) {
173 wp_delete_file( $moved['file'] );
174 } );
175 return (string) $moved['file'];
176 }
177
178 /**
179 * Stash the result and go back to the screen.
180 *
181 * @param array $report Report or ['error' => msg].
182 * @return void
183 */
184 private static function finish( array $report ): void {
185 set_transient( 'easy_invoice_import_result_' . get_current_user_id(), $report, 5 * MINUTE_IN_SECONDS );
186 wp_safe_redirect( admin_url( 'admin.php?page=' . self::PAGE_SLUG ) );
187 exit;
188 }
189 }
190