This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Class WC_Product_CSV_Importer_Controller file. |
||
4 | * |
||
5 | * @package WooCommerce\Admin\Importers |
||
6 | */ |
||
7 | |||
8 | 1 | if ( ! defined( 'ABSPATH' ) ) { |
|
9 | exit; |
||
10 | } |
||
11 | |||
12 | 1 | if ( ! class_exists( 'WP_Importer' ) ) { |
|
13 | 1 | return; |
|
14 | } |
||
15 | |||
16 | /** |
||
17 | * Product importer controller - handles file upload and forms in admin. |
||
18 | * |
||
19 | * @package WooCommerce/Admin/Importers |
||
20 | * @version 3.1.0 |
||
21 | */ |
||
22 | class WC_Product_CSV_Importer_Controller { |
||
23 | |||
24 | /** |
||
25 | * The path to the current file. |
||
26 | * |
||
27 | * @var string |
||
28 | */ |
||
29 | protected $file = ''; |
||
30 | |||
31 | /** |
||
32 | * The current import step. |
||
33 | * |
||
34 | * @var string |
||
35 | */ |
||
36 | protected $step = ''; |
||
37 | |||
38 | /** |
||
39 | * Progress steps. |
||
40 | * |
||
41 | * @var array |
||
42 | */ |
||
43 | protected $steps = array(); |
||
44 | |||
45 | /** |
||
46 | * Errors. |
||
47 | * |
||
48 | * @var array |
||
49 | */ |
||
50 | protected $errors = array(); |
||
51 | |||
52 | /** |
||
53 | * The current delimiter for the file being read. |
||
54 | * |
||
55 | * @var string |
||
56 | */ |
||
57 | protected $delimiter = ','; |
||
58 | |||
59 | /** |
||
60 | * Whether to use previous mapping selections. |
||
61 | * |
||
62 | * @var bool |
||
63 | */ |
||
64 | protected $map_preferences = false; |
||
65 | |||
66 | /** |
||
67 | * Whether to skip existing products. |
||
68 | * |
||
69 | * @var bool |
||
70 | */ |
||
71 | protected $update_existing = false; |
||
72 | |||
73 | /** |
||
74 | * Get importer instance. |
||
75 | * |
||
76 | * @param string $file File to import. |
||
77 | * @param array $args Importer arguments. |
||
78 | * @return WC_Product_CSV_Importer |
||
79 | */ |
||
80 | public static function get_importer( $file, $args = array() ) { |
||
81 | $importer_class = apply_filters( 'woocommerce_product_csv_importer_class', 'WC_Product_CSV_Importer' ); |
||
82 | $args = apply_filters( 'woocommerce_product_csv_importer_args', $args, $importer_class ); |
||
83 | return new $importer_class( $file, $args ); |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * Check whether a file is a valid CSV file. |
||
88 | * |
||
89 | * @todo Replace this method with wc_is_file_valid_csv() function. |
||
90 | * @param string $file File path. |
||
91 | * @param bool $check_path Whether to also check the file is located in a valid location (Default: true). |
||
92 | * @return bool |
||
93 | */ |
||
94 | 8 | public static function is_file_valid_csv( $file, $check_path = true ) { |
|
95 | 8 | if ( $check_path && apply_filters( 'woocommerce_product_csv_importer_check_import_file_path', true ) && false !== stripos( $file, '://' ) ) { |
|
96 | 1 | return false; |
|
97 | } |
||
98 | |||
99 | 8 | $valid_filetypes = self::get_valid_csv_filetypes(); |
|
100 | 8 | $filetype = wp_check_filetype( $file, $valid_filetypes ); |
|
101 | 8 | if ( in_array( $filetype['type'], $valid_filetypes, true ) ) { |
|
102 | 8 | return true; |
|
103 | } |
||
104 | |||
105 | 1 | return false; |
|
106 | } |
||
107 | |||
108 | /** |
||
109 | * Get all the valid filetypes for a CSV file. |
||
110 | * |
||
111 | * @return array |
||
112 | */ |
||
113 | 8 | protected static function get_valid_csv_filetypes() { |
|
114 | 8 | return apply_filters( |
|
115 | 8 | 'woocommerce_csv_product_import_valid_filetypes', |
|
116 | array( |
||
117 | 8 | 'csv' => 'text/csv', |
|
118 | 'txt' => 'text/plain', |
||
119 | ) |
||
120 | ); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Constructor. |
||
125 | */ |
||
126 | 1 | public function __construct() { |
|
127 | $default_steps = array( |
||
128 | 'upload' => array( |
||
129 | 1 | 'name' => __( 'Upload CSV file', 'woocommerce' ), |
|
130 | 1 | 'view' => array( $this, 'upload_form' ), |
|
131 | 1 | 'handler' => array( $this, 'upload_form_handler' ), |
|
132 | ), |
||
133 | 'mapping' => array( |
||
134 | 1 | 'name' => __( 'Column mapping', 'woocommerce' ), |
|
135 | 1 | 'view' => array( $this, 'mapping_form' ), |
|
136 | 1 | 'handler' => '', |
|
137 | ), |
||
138 | 'import' => array( |
||
139 | 1 | 'name' => __( 'Import', 'woocommerce' ), |
|
140 | 1 | 'view' => array( $this, 'import' ), |
|
141 | 1 | 'handler' => '', |
|
142 | ), |
||
143 | 'done' => array( |
||
144 | 1 | 'name' => __( 'Done!', 'woocommerce' ), |
|
145 | 1 | 'view' => array( $this, 'done' ), |
|
146 | 1 | 'handler' => '', |
|
147 | ), |
||
148 | ); |
||
149 | |||
150 | 1 | $this->steps = apply_filters( 'woocommerce_product_csv_importer_steps', $default_steps ); |
|
151 | |||
152 | // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification |
||
153 | 1 | $this->step = isset( $_REQUEST['step'] ) ? sanitize_key( $_REQUEST['step'] ) : current( array_keys( $this->steps ) ); |
|
154 | 1 | $this->file = isset( $_REQUEST['file'] ) ? wc_clean( wp_unslash( $_REQUEST['file'] ) ) : ''; |
|
0 ignored issues
–
show
|
|||
155 | 1 | $this->update_existing = isset( $_REQUEST['update_existing'] ) ? (bool) $_REQUEST['update_existing'] : false; |
|
156 | 1 | $this->delimiter = ! empty( $_REQUEST['delimiter'] ) ? wc_clean( wp_unslash( $_REQUEST['delimiter'] ) ) : ','; |
|
0 ignored issues
–
show
It seems like
!empty($_REQUEST['delimi...ST['delimiter'])) : ',' can also be of type array . However, the property $delimiter is declared as type string . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
Loading history...
|
|||
157 | 1 | $this->map_preferences = isset( $_REQUEST['map_preferences'] ) ? (bool) $_REQUEST['map_preferences'] : false; |
|
158 | // phpcs:enable |
||
159 | |||
160 | // Import mappings for CSV data. |
||
161 | 1 | include_once dirname( __FILE__ ) . '/mappings/mappings.php'; |
|
162 | |||
163 | 1 | if ( $this->map_preferences ) { |
|
164 | add_filter( 'woocommerce_csv_product_import_mapped_columns', array( $this, 'auto_map_user_preferences' ), 9999 ); |
||
165 | } |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Get the URL for the next step's screen. |
||
170 | * |
||
171 | * @param string $step slug (default: current step). |
||
172 | * @return string URL for next step if a next step exists. |
||
173 | * Admin URL if it's the last step. |
||
174 | * Empty string on failure. |
||
175 | */ |
||
176 | public function get_next_step_link( $step = '' ) { |
||
177 | if ( ! $step ) { |
||
178 | $step = $this->step; |
||
179 | } |
||
180 | |||
181 | $keys = array_keys( $this->steps ); |
||
182 | |||
183 | if ( end( $keys ) === $step ) { |
||
184 | return admin_url(); |
||
185 | } |
||
186 | |||
187 | $step_index = array_search( $step, $keys, true ); |
||
188 | |||
189 | if ( false === $step_index ) { |
||
190 | return ''; |
||
191 | } |
||
192 | |||
193 | $params = array( |
||
194 | 'step' => $keys[ $step_index + 1 ], |
||
195 | 'file' => str_replace( DIRECTORY_SEPARATOR, '/', $this->file ), |
||
196 | 'delimiter' => $this->delimiter, |
||
197 | 'update_existing' => $this->update_existing, |
||
198 | 'map_preferences' => $this->map_preferences, |
||
199 | '_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ), // wp_nonce_url() escapes & to & breaking redirects. |
||
200 | ); |
||
201 | |||
202 | return add_query_arg( $params ); |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Output header view. |
||
207 | */ |
||
208 | protected function output_header() { |
||
209 | include dirname( __FILE__ ) . '/views/html-csv-import-header.php'; |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Output steps view. |
||
214 | */ |
||
215 | protected function output_steps() { |
||
216 | include dirname( __FILE__ ) . '/views/html-csv-import-steps.php'; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * Output footer view. |
||
221 | */ |
||
222 | protected function output_footer() { |
||
223 | include dirname( __FILE__ ) . '/views/html-csv-import-footer.php'; |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Add error message. |
||
228 | * |
||
229 | * @param string $message Error message. |
||
230 | * @param array $actions List of actions with 'url' and 'label'. |
||
231 | */ |
||
232 | protected function add_error( $message, $actions = array() ) { |
||
233 | $this->errors[] = array( |
||
234 | 'message' => $message, |
||
235 | 'actions' => $actions, |
||
236 | ); |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Add error message. |
||
241 | */ |
||
242 | protected function output_errors() { |
||
243 | if ( ! $this->errors ) { |
||
244 | return; |
||
245 | } |
||
246 | |||
247 | foreach ( $this->errors as $error ) { |
||
248 | echo '<div class="error inline">'; |
||
249 | echo '<p>' . esc_html( $error['message'] ) . '</p>'; |
||
250 | |||
251 | if ( ! empty( $error['actions'] ) ) { |
||
252 | echo '<p>'; |
||
253 | foreach ( $error['actions'] as $action ) { |
||
254 | echo '<a class="button button-primary" href="' . esc_url( $action['url'] ) . '">' . esc_html( $action['label'] ) . '</a> '; |
||
255 | } |
||
256 | echo '</p>'; |
||
257 | } |
||
258 | echo '</div>'; |
||
259 | } |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Dispatch current step and show correct view. |
||
264 | */ |
||
265 | public function dispatch() { |
||
266 | // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification |
||
267 | View Code Duplication | if ( ! empty( $_POST['save_step'] ) && ! empty( $this->steps[ $this->step ]['handler'] ) ) { |
|
268 | call_user_func( $this->steps[ $this->step ]['handler'], $this ); |
||
269 | } |
||
270 | $this->output_header(); |
||
271 | $this->output_steps(); |
||
272 | $this->output_errors(); |
||
273 | call_user_func( $this->steps[ $this->step ]['view'], $this ); |
||
274 | $this->output_footer(); |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Output information about the uploading process. |
||
279 | */ |
||
280 | protected function upload_form() { |
||
281 | $bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() ); |
||
282 | $size = size_format( $bytes ); |
||
283 | $upload_dir = wp_upload_dir(); |
||
284 | |||
285 | include dirname( __FILE__ ) . '/views/html-product-csv-import-form.php'; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Handle the upload form and store options. |
||
290 | */ |
||
291 | public function upload_form_handler() { |
||
292 | check_admin_referer( 'woocommerce-csv-importer' ); |
||
293 | |||
294 | $file = $this->handle_upload(); |
||
295 | |||
296 | if ( is_wp_error( $file ) ) { |
||
297 | $this->add_error( $file->get_error_message() ); |
||
298 | return; |
||
299 | } else { |
||
300 | $this->file = $file; |
||
301 | } |
||
302 | |||
303 | wp_redirect( esc_url_raw( $this->get_next_step_link() ) ); |
||
304 | exit; |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * Handles the CSV upload and initial parsing of the file to prepare for |
||
309 | * displaying author import options. |
||
310 | * |
||
311 | * @return string|WP_Error |
||
312 | */ |
||
313 | 1 | public function handle_upload() { |
|
314 | // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification -- Nonce already verified in WC_Product_CSV_Importer_Controller::upload_form_handler() |
||
315 | 1 | $file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : ''; |
|
316 | |||
317 | 1 | if ( empty( $file_url ) ) { |
|
318 | if ( ! isset( $_FILES['import'] ) ) { |
||
319 | return new WP_Error( 'woocommerce_product_csv_importer_upload_file_empty', __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.', 'woocommerce' ) ); |
||
320 | } |
||
321 | |||
322 | if ( ! self::is_file_valid_csv( wc_clean( wp_unslash( $_FILES['import']['name'] ) ), false ) ) { |
||
323 | return new WP_Error( 'woocommerce_product_csv_importer_upload_file_invalid', __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) ); |
||
324 | } |
||
325 | |||
326 | $overrides = array( |
||
327 | 'test_form' => false, |
||
328 | 'mimes' => self::get_valid_csv_filetypes(), |
||
329 | ); |
||
330 | $import = $_FILES['import']; // WPCS: sanitization ok, input var ok. |
||
331 | $upload = wp_handle_upload( $import, $overrides ); |
||
332 | |||
333 | if ( isset( $upload['error'] ) ) { |
||
334 | return new WP_Error( 'woocommerce_product_csv_importer_upload_error', $upload['error'] ); |
||
335 | } |
||
336 | |||
337 | // Construct the object array. |
||
338 | $object = array( |
||
339 | 'post_title' => basename( $upload['file'] ), |
||
340 | 'post_content' => $upload['url'], |
||
341 | 'post_mime_type' => $upload['type'], |
||
342 | 'guid' => $upload['url'], |
||
343 | 'context' => 'import', |
||
344 | 'post_status' => 'private', |
||
345 | ); |
||
346 | |||
347 | // Save the data. |
||
348 | $id = wp_insert_attachment( $object, $upload['file'] ); |
||
349 | |||
350 | /* |
||
351 | * Schedule a cleanup for one day from now in case of failed |
||
352 | * import or missing wp_import_cleanup() call. |
||
353 | */ |
||
354 | wp_schedule_single_event( time() + DAY_IN_SECONDS, 'importer_scheduled_cleanup', array( $id ) ); |
||
355 | |||
356 | return $upload['file']; |
||
357 | 1 | } elseif ( file_exists( ABSPATH . $file_url ) ) { |
|
358 | 1 | if ( ! self::is_file_valid_csv( ABSPATH . $file_url ) ) { |
|
359 | return new WP_Error( 'woocommerce_product_csv_importer_upload_file_invalid', __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) ); |
||
360 | } |
||
361 | |||
362 | 1 | return ABSPATH . $file_url; |
|
363 | } |
||
364 | // phpcs:enable |
||
365 | |||
366 | return new WP_Error( 'woocommerce_product_csv_importer_upload_invalid_file', __( 'Please upload or provide the link to a valid CSV file.', 'woocommerce' ) ); |
||
367 | } |
||
368 | |||
369 | /** |
||
370 | * Mapping step. |
||
371 | */ |
||
372 | protected function mapping_form() { |
||
373 | check_admin_referer( 'woocommerce-csv-importer' ); |
||
374 | $args = array( |
||
375 | 'lines' => 1, |
||
376 | 'delimiter' => $this->delimiter, |
||
377 | ); |
||
378 | |||
379 | $importer = self::get_importer( $this->file, $args ); |
||
380 | $headers = $importer->get_raw_keys(); |
||
381 | $mapped_items = $this->auto_map_columns( $headers ); |
||
382 | $sample = current( $importer->get_raw_data() ); |
||
383 | |||
384 | if ( empty( $sample ) ) { |
||
385 | $this->add_error( |
||
386 | __( 'The file is empty or using a different encoding than UTF-8, please try again with a new file.', 'woocommerce' ), |
||
387 | array( |
||
388 | array( |
||
389 | 'url' => admin_url( 'edit.php?post_type=product&page=product_importer' ), |
||
390 | 'label' => __( 'Upload a new file', 'woocommerce' ), |
||
391 | ), |
||
392 | ) |
||
393 | ); |
||
394 | |||
395 | // Force output the errors in the same page. |
||
396 | $this->output_errors(); |
||
397 | return; |
||
398 | } |
||
399 | |||
400 | include_once dirname( __FILE__ ) . '/views/html-csv-import-mapping.php'; |
||
401 | } |
||
402 | |||
403 | /** |
||
404 | * Import the file if it exists and is valid. |
||
405 | */ |
||
406 | public function import() { |
||
407 | // Displaying this page triggers Ajax action to run the import with a valid nonce, |
||
408 | // therefore this page needs to be nonce protected as well. |
||
409 | check_admin_referer( 'woocommerce-csv-importer' ); |
||
410 | |||
411 | if ( ! self::is_file_valid_csv( $this->file ) ) { |
||
412 | $this->add_error( __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) ); |
||
413 | $this->output_errors(); |
||
414 | return; |
||
415 | } |
||
416 | |||
417 | if ( ! is_file( $this->file ) ) { |
||
418 | $this->add_error( __( 'The file does not exist, please try again.', 'woocommerce' ) ); |
||
419 | $this->output_errors(); |
||
420 | return; |
||
421 | } |
||
422 | |||
423 | if ( ! empty( $_POST['map_from'] ) && ! empty( $_POST['map_to'] ) ) { |
||
424 | $mapping_from = wc_clean( wp_unslash( $_POST['map_from'] ) ); |
||
425 | $mapping_to = wc_clean( wp_unslash( $_POST['map_to'] ) ); |
||
426 | |||
427 | // Save mapping preferences for future imports. |
||
428 | update_user_option( get_current_user_id(), 'woocommerce_product_import_mapping', $mapping_to ); |
||
429 | } else { |
||
430 | wp_redirect( esc_url_raw( $this->get_next_step_link( 'upload' ) ) ); |
||
431 | exit; |
||
432 | } |
||
433 | |||
434 | wp_localize_script( |
||
435 | 'wc-product-import', |
||
436 | 'wc_product_import_params', |
||
437 | array( |
||
438 | 'import_nonce' => wp_create_nonce( 'wc-product-import' ), |
||
439 | 'mapping' => array( |
||
440 | 'from' => $mapping_from, |
||
441 | 'to' => $mapping_to, |
||
442 | ), |
||
443 | 'file' => $this->file, |
||
444 | 'update_existing' => $this->update_existing, |
||
445 | 'delimiter' => $this->delimiter, |
||
446 | ) |
||
447 | ); |
||
448 | wp_enqueue_script( 'wc-product-import' ); |
||
449 | |||
450 | include_once dirname( __FILE__ ) . '/views/html-csv-import-progress.php'; |
||
451 | } |
||
452 | |||
453 | /** |
||
454 | * Done step. |
||
455 | */ |
||
456 | protected function done() { |
||
457 | check_admin_referer( 'woocommerce-csv-importer' ); |
||
458 | $imported = isset( $_GET['products-imported'] ) ? absint( $_GET['products-imported'] ) : 0; |
||
459 | $updated = isset( $_GET['products-updated'] ) ? absint( $_GET['products-updated'] ) : 0; |
||
460 | $failed = isset( $_GET['products-failed'] ) ? absint( $_GET['products-failed'] ) : 0; |
||
461 | $skipped = isset( $_GET['products-skipped'] ) ? absint( $_GET['products-skipped'] ) : 0; |
||
462 | $errors = array_filter( (array) get_user_option( 'product_import_error_log' ) ); |
||
463 | |||
464 | include_once dirname( __FILE__ ) . '/views/html-csv-import-done.php'; |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * Columns to normalize. |
||
469 | * |
||
470 | * @param array $columns List of columns names and keys. |
||
471 | * @return array |
||
472 | */ |
||
473 | protected function normalize_columns_names( $columns ) { |
||
474 | $normalized = array(); |
||
475 | |||
476 | foreach ( $columns as $key => $value ) { |
||
477 | $normalized[ strtolower( $key ) ] = $value; |
||
478 | } |
||
479 | |||
480 | return $normalized; |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Auto map column names. |
||
485 | * |
||
486 | * @param array $raw_headers Raw header columns. |
||
487 | * @param bool $num_indexes If should use numbers or raw header columns as indexes. |
||
488 | * @return array |
||
489 | */ |
||
490 | protected function auto_map_columns( $raw_headers, $num_indexes = true ) { |
||
491 | $weight_unit = get_option( 'woocommerce_weight_unit' ); |
||
492 | $dimension_unit = get_option( 'woocommerce_dimension_unit' ); |
||
493 | |||
494 | /* |
||
495 | * @hooked wc_importer_generic_mappings - 10 |
||
496 | * @hooked wc_importer_wordpress_mappings - 10 |
||
497 | * @hooked wc_importer_default_english_mappings - 100 |
||
498 | */ |
||
499 | $default_columns = $this->normalize_columns_names( |
||
500 | apply_filters( |
||
501 | 'woocommerce_csv_product_import_mapping_default_columns', |
||
502 | array( |
||
503 | __( 'ID', 'woocommerce' ) => 'id', |
||
504 | __( 'Type', 'woocommerce' ) => 'type', |
||
505 | __( 'SKU', 'woocommerce' ) => 'sku', |
||
506 | __( 'Name', 'woocommerce' ) => 'name', |
||
507 | __( 'Published', 'woocommerce' ) => 'published', |
||
508 | __( 'Is featured?', 'woocommerce' ) => 'featured', |
||
509 | __( 'Visibility in catalog', 'woocommerce' ) => 'catalog_visibility', |
||
510 | __( 'Short description', 'woocommerce' ) => 'short_description', |
||
511 | __( 'Description', 'woocommerce' ) => 'description', |
||
512 | __( 'Date sale price starts', 'woocommerce' ) => 'date_on_sale_from', |
||
513 | __( 'Date sale price ends', 'woocommerce' ) => 'date_on_sale_to', |
||
514 | __( 'Tax status', 'woocommerce' ) => 'tax_status', |
||
515 | __( 'Tax class', 'woocommerce' ) => 'tax_class', |
||
516 | __( 'In stock?', 'woocommerce' ) => 'stock_status', |
||
517 | __( 'Stock', 'woocommerce' ) => 'stock_quantity', |
||
518 | __( 'Backorders allowed?', 'woocommerce' ) => 'backorders', |
||
519 | __( 'Low stock amount', 'woocommerce' ) => 'low_stock_amount', |
||
520 | __( 'Sold individually?', 'woocommerce' ) => 'sold_individually', |
||
521 | /* translators: %s: Weight unit */ |
||
522 | sprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit ) => 'weight', |
||
523 | /* translators: %s: Length unit */ |
||
524 | sprintf( __( 'Length (%s)', 'woocommerce' ), $dimension_unit ) => 'length', |
||
525 | /* translators: %s: Width unit */ |
||
526 | sprintf( __( 'Width (%s)', 'woocommerce' ), $dimension_unit ) => 'width', |
||
527 | /* translators: %s: Height unit */ |
||
528 | sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit ) => 'height', |
||
529 | __( 'Allow customer reviews?', 'woocommerce' ) => 'reviews_allowed', |
||
530 | __( 'Purchase note', 'woocommerce' ) => 'purchase_note', |
||
531 | __( 'Sale price', 'woocommerce' ) => 'sale_price', |
||
532 | __( 'Regular price', 'woocommerce' ) => 'regular_price', |
||
533 | __( 'Categories', 'woocommerce' ) => 'category_ids', |
||
534 | __( 'Tags', 'woocommerce' ) => 'tag_ids', |
||
535 | __( 'Shipping class', 'woocommerce' ) => 'shipping_class_id', |
||
536 | __( 'Images', 'woocommerce' ) => 'images', |
||
537 | __( 'Download limit', 'woocommerce' ) => 'download_limit', |
||
538 | __( 'Download expiry days', 'woocommerce' ) => 'download_expiry', |
||
539 | __( 'Parent', 'woocommerce' ) => 'parent_id', |
||
540 | __( 'Upsells', 'woocommerce' ) => 'upsell_ids', |
||
541 | __( 'Cross-sells', 'woocommerce' ) => 'cross_sell_ids', |
||
542 | __( 'Grouped products', 'woocommerce' ) => 'grouped_products', |
||
543 | __( 'External URL', 'woocommerce' ) => 'product_url', |
||
544 | __( 'Button text', 'woocommerce' ) => 'button_text', |
||
545 | __( 'Position', 'woocommerce' ) => 'menu_order', |
||
546 | ), |
||
547 | $raw_headers |
||
548 | ) |
||
549 | ); |
||
550 | |||
551 | $special_columns = $this->get_special_columns( |
||
552 | $this->normalize_columns_names( |
||
553 | apply_filters( |
||
554 | 'woocommerce_csv_product_import_mapping_special_columns', |
||
555 | array( |
||
556 | /* translators: %d: Attribute number */ |
||
557 | __( 'Attribute %d name', 'woocommerce' ) => 'attributes:name', |
||
558 | /* translators: %d: Attribute number */ |
||
559 | __( 'Attribute %d value(s)', 'woocommerce' ) => 'attributes:value', |
||
560 | /* translators: %d: Attribute number */ |
||
561 | __( 'Attribute %d visible', 'woocommerce' ) => 'attributes:visible', |
||
562 | /* translators: %d: Attribute number */ |
||
563 | __( 'Attribute %d global', 'woocommerce' ) => 'attributes:taxonomy', |
||
564 | /* translators: %d: Attribute number */ |
||
565 | __( 'Attribute %d default', 'woocommerce' ) => 'attributes:default', |
||
566 | /* translators: %d: Download number */ |
||
567 | __( 'Download %d name', 'woocommerce' ) => 'downloads:name', |
||
568 | /* translators: %d: Download number */ |
||
569 | __( 'Download %d URL', 'woocommerce' ) => 'downloads:url', |
||
570 | /* translators: %d: Meta number */ |
||
571 | __( 'Meta: %s', 'woocommerce' ) => 'meta:', |
||
572 | ), |
||
573 | $raw_headers |
||
574 | ) |
||
575 | ) |
||
576 | ); |
||
577 | |||
578 | $headers = array(); |
||
579 | foreach ( $raw_headers as $key => $field ) { |
||
580 | $field = strtolower( $field ); |
||
581 | $index = $num_indexes ? $key : $field; |
||
582 | $headers[ $index ] = $field; |
||
583 | |||
584 | if ( isset( $default_columns[ $field ] ) ) { |
||
585 | $headers[ $index ] = $default_columns[ $field ]; |
||
586 | } else { |
||
587 | foreach ( $special_columns as $regex => $special_key ) { |
||
588 | if ( preg_match( $regex, $field, $matches ) ) { |
||
589 | $headers[ $index ] = $special_key . $matches[1]; |
||
590 | break; |
||
591 | } |
||
592 | } |
||
593 | } |
||
594 | } |
||
595 | |||
596 | return apply_filters( 'woocommerce_csv_product_import_mapped_columns', $headers, $raw_headers ); |
||
597 | } |
||
598 | |||
599 | /** |
||
600 | * Map columns using the user's lastest import mappings. |
||
601 | * |
||
602 | * @param array $headers Header columns. |
||
603 | * @return array |
||
604 | */ |
||
605 | public function auto_map_user_preferences( $headers ) { |
||
606 | $mapping_preferences = get_user_option( 'woocommerce_product_import_mapping' ); |
||
607 | |||
608 | if ( ! empty( $mapping_preferences ) && is_array( $mapping_preferences ) ) { |
||
609 | return $mapping_preferences; |
||
610 | } |
||
611 | |||
612 | return $headers; |
||
613 | } |
||
614 | |||
615 | /** |
||
616 | * Sanitize special column name regex. |
||
617 | * |
||
618 | * @param string $value Raw special column name. |
||
619 | * @return string |
||
620 | */ |
||
621 | protected function sanitize_special_column_name_regex( $value ) { |
||
622 | return '/' . str_replace( array( '%d', '%s' ), '(.*)', trim( quotemeta( $value ) ) ) . '/'; |
||
623 | } |
||
624 | |||
625 | /** |
||
626 | * Get special columns. |
||
627 | * |
||
628 | * @param array $columns Raw special columns. |
||
629 | * @return array |
||
630 | */ |
||
631 | protected function get_special_columns( $columns ) { |
||
632 | $formatted = array(); |
||
633 | |||
634 | foreach ( $columns as $key => $value ) { |
||
635 | $regex = $this->sanitize_special_column_name_regex( $key ); |
||
636 | |||
637 | $formatted[ $regex ] = $value; |
||
638 | } |
||
639 | |||
640 | return $formatted; |
||
641 | } |
||
642 | |||
643 | /** |
||
644 | * Get mapping options. |
||
645 | * |
||
646 | * @param string $item Item name. |
||
647 | * @return array |
||
648 | */ |
||
649 | protected function get_mapping_options( $item = '' ) { |
||
650 | // Get index for special column names. |
||
651 | $index = $item; |
||
652 | |||
653 | if ( preg_match( '/\d+/', $item, $matches ) ) { |
||
654 | $index = $matches[0]; |
||
655 | } |
||
656 | |||
657 | // Properly format for meta field. |
||
658 | $meta = str_replace( 'meta:', '', $item ); |
||
659 | |||
660 | // Available options. |
||
661 | $weight_unit = get_option( 'woocommerce_weight_unit' ); |
||
662 | $dimension_unit = get_option( 'woocommerce_dimension_unit' ); |
||
663 | $options = array( |
||
664 | 'id' => __( 'ID', 'woocommerce' ), |
||
665 | 'type' => __( 'Type', 'woocommerce' ), |
||
666 | 'sku' => __( 'SKU', 'woocommerce' ), |
||
667 | 'name' => __( 'Name', 'woocommerce' ), |
||
668 | 'published' => __( 'Published', 'woocommerce' ), |
||
669 | 'featured' => __( 'Is featured?', 'woocommerce' ), |
||
670 | 'catalog_visibility' => __( 'Visibility in catalog', 'woocommerce' ), |
||
671 | 'short_description' => __( 'Short description', 'woocommerce' ), |
||
672 | 'description' => __( 'Description', 'woocommerce' ), |
||
673 | 'price' => array( |
||
674 | 'name' => __( 'Price', 'woocommerce' ), |
||
675 | 'options' => array( |
||
676 | 'regular_price' => __( 'Regular price', 'woocommerce' ), |
||
677 | 'sale_price' => __( 'Sale price', 'woocommerce' ), |
||
678 | 'date_on_sale_from' => __( 'Date sale price starts', 'woocommerce' ), |
||
679 | 'date_on_sale_to' => __( 'Date sale price ends', 'woocommerce' ), |
||
680 | ), |
||
681 | ), |
||
682 | 'tax_status' => __( 'Tax status', 'woocommerce' ), |
||
683 | 'tax_class' => __( 'Tax class', 'woocommerce' ), |
||
684 | 'stock_status' => __( 'In stock?', 'woocommerce' ), |
||
685 | 'stock_quantity' => _x( 'Stock', 'Quantity in stock', 'woocommerce' ), |
||
686 | 'backorders' => __( 'Backorders allowed?', 'woocommerce' ), |
||
687 | 'low_stock_amount' => __( 'Low stock amount', 'woocommerce' ), |
||
688 | 'sold_individually' => __( 'Sold individually?', 'woocommerce' ), |
||
689 | /* translators: %s: weight unit */ |
||
690 | 'weight' => sprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit ), |
||
691 | 'dimensions' => array( |
||
692 | 'name' => __( 'Dimensions', 'woocommerce' ), |
||
693 | 'options' => array( |
||
694 | /* translators: %s: dimension unit */ |
||
695 | 'length' => sprintf( __( 'Length (%s)', 'woocommerce' ), $dimension_unit ), |
||
696 | /* translators: %s: dimension unit */ |
||
697 | 'width' => sprintf( __( 'Width (%s)', 'woocommerce' ), $dimension_unit ), |
||
698 | /* translators: %s: dimension unit */ |
||
699 | 'height' => sprintf( __( 'Height (%s)', 'woocommerce' ), $dimension_unit ), |
||
700 | ), |
||
701 | ), |
||
702 | 'category_ids' => __( 'Categories', 'woocommerce' ), |
||
703 | 'tag_ids' => __( 'Tags (comma separated)', 'woocommerce' ), |
||
704 | 'tag_ids_spaces' => __( 'Tags (space separated)', 'woocommerce' ), |
||
705 | 'shipping_class_id' => __( 'Shipping class', 'woocommerce' ), |
||
706 | 'images' => __( 'Images', 'woocommerce' ), |
||
707 | 'parent_id' => __( 'Parent', 'woocommerce' ), |
||
708 | 'upsell_ids' => __( 'Upsells', 'woocommerce' ), |
||
709 | 'cross_sell_ids' => __( 'Cross-sells', 'woocommerce' ), |
||
710 | 'grouped_products' => __( 'Grouped products', 'woocommerce' ), |
||
711 | 'external' => array( |
||
712 | 'name' => __( 'External product', 'woocommerce' ), |
||
713 | 'options' => array( |
||
714 | 'product_url' => __( 'External URL', 'woocommerce' ), |
||
715 | 'button_text' => __( 'Button text', 'woocommerce' ), |
||
716 | ), |
||
717 | ), |
||
718 | 'downloads' => array( |
||
719 | 'name' => __( 'Downloads', 'woocommerce' ), |
||
720 | 'options' => array( |
||
721 | 'downloads:name' . $index => __( 'Download name', 'woocommerce' ), |
||
722 | 'downloads:url' . $index => __( 'Download URL', 'woocommerce' ), |
||
723 | 'download_limit' => __( 'Download limit', 'woocommerce' ), |
||
724 | 'download_expiry' => __( 'Download expiry days', 'woocommerce' ), |
||
725 | ), |
||
726 | ), |
||
727 | 'attributes' => array( |
||
728 | 'name' => __( 'Attributes', 'woocommerce' ), |
||
729 | 'options' => array( |
||
730 | 'attributes:name' . $index => __( 'Attribute name', 'woocommerce' ), |
||
731 | 'attributes:value' . $index => __( 'Attribute value(s)', 'woocommerce' ), |
||
732 | 'attributes:taxonomy' . $index => __( 'Is a global attribute?', 'woocommerce' ), |
||
733 | 'attributes:visible' . $index => __( 'Attribute visibility', 'woocommerce' ), |
||
734 | 'attributes:default' . $index => __( 'Default attribute', 'woocommerce' ), |
||
735 | ), |
||
736 | ), |
||
737 | 'reviews_allowed' => __( 'Allow customer reviews?', 'woocommerce' ), |
||
738 | 'purchase_note' => __( 'Purchase note', 'woocommerce' ), |
||
739 | 'meta:' . $meta => __( 'Import as meta data', 'woocommerce' ), |
||
740 | 'menu_order' => __( 'Position', 'woocommerce' ), |
||
741 | ); |
||
742 | |||
743 | return apply_filters( 'woocommerce_csv_product_import_mapping_options', $options, $item ); |
||
744 | } |
||
745 | } |
||
746 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.