PluginProbe
UsersWP – Front-end login form, User Registration, User Profile & Members Directory plugin for WP / 1.2.66
UsersWP – Front-end login form, User Registration, User Profile & Members Directory plugin for WP v1.2.66
1.2.72 1.2.71 1.2.70 1.2.69 1.2.68 1.2.67 1.2.66 1.2.65 1.2.64 1.2.63 trunk 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.23 All 172 releases
userswp / includes / class-import-export.php

class-import-export.php in UsersWP – Front-end login form, User Registration, User Profile & Members Directory plugin for WP 1.2.66, at includes/class-import-export.php

961 lines 32.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * UsersWP Notice display functions.
4 *
5 * All UsersWP notice display related functions can be found here.
6 *
7 * @since 1.0.0
8 * @author GeoDirectory Team <info@wpgeodirectory.com>
9 */
10 class UsersWP_Import_Export {
11 private $wp_filesystem;
12 private $export_dir;
13 private $export_url;
14 public $per_page;
15 public $meta_table_name;
16 public $path;
17 public $total_rows;
18 public $imp_step;
19 public $skipped;
20 private $empty;
21 private $step;
22 private $file;
23 private $filename;
24
25 /**
26 * Columns that are NEVER writable via CSV import.
27 * Denylist wins over allowlist — a column here can never be accidentally
28 * re-enabled by adding it to $import_meta_allowlist.
29 *
30 * @var array
31 */
32 private static $import_meta_denylist = array(
33 'user_id', // Primary key — never importable.
34 'old_password', // Credential-adjacent — must not be set via import.
35 );
36
37 /**
38 * Columns that ARE permitted in a CSV import (positive / allowlist).
39 * Everything not listed here is silently skipped — default-deny.
40 * Add new safe meta keys here deliberately; never use a wildcard.
41 *
42 * @var array
43 */
44 private static $import_meta_allowlist = array(
45 // Core WP user fields
46 'username',
47 'email',
48 'display_name',
49 'first_name',
50 'last_name',
51 'description',
52 'user_url',
53 'user_registered',
54 'role',
55 // uwp_usermeta safe fields
56 'bio',
57 'phone',
58 'user_privacy',
59 'avatar_thumb',
60 'banner_thumb',
61 );
62
63
64 public function __construct() {
65 global $wp_filesystem;
66
67 if ( empty( $wp_filesystem ) ) {
68 require_once( ABSPATH . '/wp-admin/includes/file.php' );
69 WP_Filesystem();
70 global $wp_filesystem;
71 }
72
73 $this->wp_filesystem = $wp_filesystem;
74 $this->export_dir = $this->export_location();
75 $this->export_url = $this->export_location( true );
76 $this->per_page = apply_filters('uwp_import_export_per_page', 20, $this);
77 $this->meta_table_name = get_usermeta_table_prefix() . 'uwp_usermeta';
78 $this->path = '';
79
80 add_action( 'admin_init', array($this, 'process_settings_export') );
81 add_action( 'admin_init', array($this, 'process_settings_import') );
82 add_action( 'wp_ajax_uwp_ajax_export_users', array( $this, 'process_users_export' ) );
83 add_action( 'wp_ajax_uwp_ajax_import_users', array( $this, 'process_users_import' ) );
84 add_action( 'wp_ajax_uwp_ie_upload_file', array( $this, 'ie_upload_file' ) );
85 add_action( 'admin_notices', array($this, 'ie_admin_notice') );
86 add_filter( 'uwp_get_export_users_status', array( $this, 'get_export_users_status' ) );
87 add_filter( 'uwp_get_import_users_status', array( $this, 'get_import_users_status' ) );
88 }
89
90 /**
91 * Returns export file location
92 *
93 * @package userswp
94 *
95 * @param bool $relative
96 *
97 * @return string
98 *
99 */
100 public function export_location( $relative = false ) {
101 $upload_dir = wp_upload_dir();
102 $export_location = $relative ? trailingslashit( $upload_dir['baseurl'] ) . 'cache' : trailingslashit( $upload_dir['basedir'] ) . 'cache';
103 $export_location = apply_filters( 'uwp_export_location', $export_location, $relative );
104
105 return trailingslashit( $export_location );
106 }
107
108 /**
109 * Displays notice
110 */
111 public function ie_admin_notice(){
112 if(isset($_GET['imp-msg']) && 'success' == $_GET['imp-msg']){
113 ?>
114 <div class="notice notice-success is-dismissible">
115 <p><?php esc_html_e( 'Settings imported successfully!', 'userswp' ); ?></p>
116 </div>
117 <?php
118 }
119 }
120
121 /**
122 * Process a settings export that generates a .json file of the settings
123 */
124 public function process_settings_export() {
125 if( empty( $_POST['uwp_ie_action'] ) || 'export_settings' != $_POST['uwp_ie_action'] )
126 return;
127 if( ! wp_verify_nonce( $_POST['uwp_export_nonce'], 'uwp_export_nonce' ) )
128 return;
129 if( ! current_user_can( 'manage_options' ) )
130 return;
131 $settings = get_option( 'uwp_settings' );
132 ignore_user_abort( true );
133 nocache_headers();
134 header( 'Content-Type: application/json; charset=utf-8' );
135 header( 'Content-Disposition: attachment; filename=uwp-settings-export-' . date( 'm-d-Y' ) . '.json' );
136 header( "Expires: 0" );
137 echo json_encode( $settings );
138 exit;
139 }
140
141 /**
142 * Process a settings import from a json file
143 */
144 public function process_settings_import() {
145 if( empty( $_POST['uwp_ie_action'] ) || 'import_settings' != $_POST['uwp_ie_action'] )
146 return;
147 if( ! wp_verify_nonce( $_POST['uwp_import_nonce'], 'uwp_import_nonce' ) )
148 return;
149 if( ! current_user_can( 'manage_options' ) )
150 return;
151 $extension = explode( '.', $_FILES['import_file']['name'] );
152 $extension = end( $extension );
153 if( $extension != 'json' ) {
154 wp_die( esc_html( wp_sprintf( __( 'Please upload a valid .json file. %sGo Back%s', 'userswp' ), '<a href="' . esc_url( admin_url( 'admin.php?page=userswp&tab=import-export&section=settings' ) ) . '">', '</a>' ) ) );
155 }
156 $import_file = $_FILES['import_file']['tmp_name'];
157 if( empty( $import_file ) ) {
158 wp_die( esc_html( wp_sprintf( __( 'Please upload a file to import. %sGo Back%s', 'userswp' ), '<a href="' . esc_url( admin_url( 'admin.php?page=userswp&tab=import-export&section=settings' ) ) . '">', '</a>' ) ) );
159 }
160 // Retrieve the settings from the file and convert the json object to an array.
161 $settings = (array) json_decode( file_get_contents( $import_file ), true );
162 update_option( 'uwp_settings', $settings );
163 wp_safe_redirect( admin_url( 'admin.php?page=userswp&tab=import-export&section=settings&imp-msg=success' ) ); exit;
164 }
165
166 /**
167 * Processes users export
168 */
169 public function process_users_export(){
170
171 $response = array();
172 $response['success'] = false;
173 $response['msg'] = __( 'Invalid export request found.', 'userswp' );
174
175 if ( empty( $_POST['data'] ) || !current_user_can( 'manage_options' ) ) {
176 wp_send_json( $response );
177 }
178
179 parse_str( $_POST['data'], $data );
180
181 $data['step'] = !empty( $_POST['step'] ) ? absint( $_POST['step'] ) : 1;
182
183 $_REQUEST = (array)$data;
184 if ( !( !empty( $_REQUEST['uwp_export_users_nonce'] ) && wp_verify_nonce( $_REQUEST['uwp_export_users_nonce'], 'uwp_export_users_nonce' ) ) ) {
185 $response['msg'] = __( 'Security check failed.', 'userswp' );
186 wp_send_json( $response );
187 }
188
189 if ( ( $error = $this->check_export_location() ) !== true ) {
190 $response['msg'] = __( 'Filesystem ERROR: ' . $error, 'userswp' );
191 wp_send_json( $response );
192 }
193
194 $this->set_export_params( $_REQUEST );
195
196 $return = $this->process_export_step();
197 $done = $this->get_export_status();
198
199 if ( $return ) {
200 $this->step += 1;
201
202 $response['success'] = true;
203 $response['msg'] = '';
204
205 if ( $done >= 100 ) {
206 $this->step = 'done';
207 $new_filename = 'uwp-users-export-' . date( 'y-m-d-H-i' ) . '-' . wp_generate_password(12, false ) . '.csv';
208 $new_file = $this->export_dir . $new_filename;
209
210 if ( file_exists( $this->file ) ) {
211 $this->wp_filesystem->move( $this->file, $new_file, true );
212 }
213
214 if ( file_exists( $new_file ) ) {
215 $response['data']['file'] = array( 'u' => $this->export_url . $new_filename, 's' => size_format( filesize( $new_file ), 2 ) );
216 }
217 }
218
219 $response['data']['step'] = $this->step;
220 $response['data']['done'] = $done;
221 } else {
222 $response['msg'] = __( 'No data found for export.', 'userswp' );
223 }
224
225 wp_send_json( $response );
226
227 }
228
229 /**
230 * Sets export params
231 *
232 * @package userswp
233 *
234 * @param array $request
235 *
236 */
237 public function set_export_params( $request ) {
238 $this->empty = false;
239 $this->step = !empty( $request['step'] ) ? absint( $request['step'] ) : 1;
240 $this->filename = 'uwp-users-export-temp.csv';
241 $this->file = $this->export_dir . $this->filename;
242 $chunk_per_page = !empty( $request['uwp_ie_chunk_size'] ) ? absint( $request['uwp_ie_chunk_size'] ) : 0;
243 $this->per_page = $chunk_per_page < 50 || $chunk_per_page > 100000 ? 5000 : $chunk_per_page;
244
245 do_action( 'uwp_export_users_set_params', $request );
246 }
247
248 /**
249 * Returns export file location
250 *
251 * @package userswp
252 *
253 * @return bool
254 *
255 */
256 public function check_export_location() {
257 try {
258 if ( empty( $this->wp_filesystem ) ) {
259 return __( 'Filesystem ERROR: Could not access filesystem.', 'userswp' );
260 }
261
262 if ( is_wp_error( $this->wp_filesystem ) ) {
263 return __( 'Filesystem ERROR: ' . $this->wp_filesystem->get_error_message(), 'userswp' );
264 }
265
266 $is_dir = $this->wp_filesystem->is_dir( $this->export_dir );
267 $is_writeable = $is_dir && is_writeable( $this->export_dir );
268
269 if ( $is_dir && $is_writeable ) {
270
271 } else if ( $is_dir && !$is_writeable ) {
272 if ( !$this->wp_filesystem->chmod( $this->export_dir, FS_CHMOD_DIR ) ) {
273 return wp_sprintf( __( 'Filesystem ERROR: Export location %s is not writable, check your file permissions.', 'userswp' ), $this->export_dir );
274 }
275 } else {
276 if ( !$this->wp_filesystem->mkdir( $this->export_dir, FS_CHMOD_DIR ) ) {
277 return wp_sprintf( __( 'Filesystem ERROR: Could not create directory %s. This is usually due to inconsistent file permissions.', 'userswp' ), $this->export_dir );
278 }
279 }
280
281 if(!$this->wp_filesystem->exists( $this->export_dir . '/index.php')){
282 $this->wp_filesystem->copy( USERSWP_PATH . 'assets/index.php', $this->export_dir . '/index.php' );
283 }
284
285 return true;
286 } catch ( Exception $e ) {
287 return $e->getMessage();
288 }
289 }
290
291 /**
292 * Processes export step
293 *
294 * @return bool
295 *
296 */
297 public function process_export_step() {
298 if ( $this->step < 2 ) {
299 @unlink( $this->file );
300 $this->print_columns();
301 }
302
303 $return = $this->print_rows();
304
305 if ( $return ) {
306 return true;
307 } else {
308 return false;
309 }
310 }
311
312 /**
313 * Prints columns
314 *
315 * @return array
316 *
317 */
318 public function print_columns() {
319 $column_data = '';
320 $columns = $this->get_columns();
321 $i = 1;
322 foreach( $columns as $key => $column ) {
323 $column_data .= '"' . addslashes( $column ) . '"';
324 $column_data .= $i == count( $columns ) ? '' : ',';
325 $i++;
326 }
327 $column_data .= "\r\n";
328
329 $this->attach_export_data( $column_data );
330
331 return $column_data;
332 }
333
334 /**
335 * Returns export columns
336 *
337 * @return array
338 *
339 */
340 public function get_columns() {
341 global $wpdb;
342 $columns = array();
343
344 foreach ( $wpdb->get_col( "DESC " . $this->meta_table_name, 0 ) as $column_name ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
345 $columns[] = $column_name;
346 }
347
348 return apply_filters( 'uwp_export_users_get_columns', $columns );
349 }
350
351 /**
352 * Returns export data
353 *
354 * @package userswp
355 *
356 * @return array
357 *
358 */
359 public function get_export_data() {
360 global $wpdb;
361 if(!$this->step){
362 $page = 1;
363 } else {
364 $page = $this->step;
365 }
366
367 $page_start = absint( ( $page - 1 ) * $this->per_page );
368 $data = $wpdb->get_results( "SELECT * FROM $this->meta_table_name WHERE 1=1 LIMIT $page_start,". $this->per_page); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
369 $i = 0;
370
371 foreach ($data as $u){
372 $user = get_userdata($u->user_id);
373 $data[$i]->username = $user->user_login;
374 $data[$i]->email = $user->user_email;
375 $data[$i]->bio = $user->description;
376 $i++;
377 }
378
379 return apply_filters( 'uwp_export_users_get_data', $data );
380 }
381
382 /**
383 * Returns export status
384 *
385 * @return int
386 *
387 */
388 public function get_export_status() {
389 $status = 100;
390 return apply_filters( 'uwp_get_export_users_status', $status );
391 }
392
393 /**
394 * Prints CSV rows
395 *
396 * @return string
397 *
398 */
399 public function print_rows() {
400 $row_data = '';
401 $data = $this->get_export_data();
402 $columns = $this->get_columns();
403
404 if ( is_array($data) && !empty($data) ) {
405 foreach ( $data as $row ) {
406 $i = 1;
407 foreach ( $row as $key => $column ) {
408 $column = $this->escape_data( $column );
409 $row_data .= '"' . addslashes( preg_replace( "/\"/","'", $column ) ) . '"';
410 $row_data .= $i == count( $columns ) ? '' : ',';
411 $i++;
412 }
413 $row_data .= "\r\n";
414 }
415
416 $this->attach_export_data( $row_data );
417
418 return $row_data;
419 }
420
421 return false;
422 }
423
424 /**
425 * Returns export file content
426 *
427 * @return string
428 *
429 */
430 protected function get_export_file() {
431 $file = '';
432
433 if ( $this->wp_filesystem->exists( $this->file ) ) {
434 $file = $this->wp_filesystem->get_contents( $this->file );
435 } else {
436 $this->wp_filesystem->put_contents( $this->file, '' );
437 }
438
439 return $file;
440 }
441
442 /**
443 * Adds export data to CSV file
444 *
445 * @package userswp
446 *
447 * @param string $data
448 *
449 */
450 protected function attach_export_data( $data = '' ) {
451 $filedata = $this->get_export_file();
452 $filedata .= $data;
453
454 $this->wp_filesystem->put_contents( $this->file, $filedata );
455
456 $rows = file( $this->file, FILE_SKIP_EMPTY_LINES );
457 $columns = $this->get_columns();
458 $columns = empty( $columns ) ? 0 : 1;
459
460 $this->empty = count( $rows ) == $columns ? true : false;
461 }
462
463 /**
464 * Returns export user status
465 *
466 * @return int
467 *
468 */
469 public function get_export_users_status() {
470 global $wpdb;
471 $data = $wpdb->get_results("SELECT user_id FROM $this->meta_table_name WHERE 1=1"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
472 $total = !empty( $data ) ? count( $data ) : 0;
473 $status = 100;
474
475 if ( $this->per_page > $total ) {
476 $this->per_page = $total;
477 }
478
479 if ( $total > 0 ) {
480 $status = ( ( $this->per_page * $this->step ) / $total ) * 100;
481 }
482
483 if ( $status > 100 ) {
484 $status = 100;
485 }
486
487 return $status;
488 }
489
490 /**
491 * Uploaded file handling
492 *
493 * @return string
494 *
495 */
496 public function ie_upload_file(){
497
498 if ( ! current_user_can( 'manage_options' ) ) {
499 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'userswp' ) ), 403 );
500 }
501
502 check_ajax_referer( 'uwp-ie-file-upload-nonce', 'nonce' );
503
504 $upload_data = array(
505 'name' => $_FILES['import_file']['name'],
506 'type' => $_FILES['import_file']['type'],
507 'tmp_name' => $_FILES['import_file']['tmp_name'],
508 'error' => $_FILES['import_file']['error'],
509 'size' => $_FILES['import_file']['size']
510 );
511
512 header('Content-Type: text/html; charset=' . get_option('blog_charset'));
513
514 add_filter( 'upload_mimes', array( $this, 'allowed_upload_mimes' ) );
515 $uploaded_file = wp_handle_upload( $upload_data, array('test_form' => false) );
516 remove_filter( 'upload_mimes', array( $this, 'allowed_upload_mimes' ) );
517
518 if ( isset( $uploaded_file['url'] ) ) {
519 $file_loc = $uploaded_file['url'];
520 echo esc_url( $file_loc );exit;
521 } else {
522 echo 'error';
523 }
524 exit;
525 }
526
527 /**
528 * Processes users import
529 *
530 */
531 public function process_users_import(){
532
533 $response = array();
534 $response['success'] = false;
535 $response['msg'] = __( 'Invalid import request found.', 'userswp' );
536
537 if ( empty( $_POST['data'] ) || !current_user_can( 'manage_options' ) ) {
538 wp_send_json( $response );
539 }
540
541 parse_str( $_POST['data'], $data );
542
543 $this->imp_step = !empty( $_POST['step'] ) ? absint( $_POST['step'] ) : 1;
544
545 $_REQUEST = (array)$data;
546 if ( !( !empty( $_REQUEST['uwp_import_users_nonce'] ) && wp_verify_nonce( $_REQUEST['uwp_import_users_nonce'], 'uwp_import_users_nonce' ) ) ) {
547 $response['msg'] = __( 'Security check failed.', 'userswp' );
548 wp_send_json( $response );
549 }
550
551 $allowed = array('csv');
552 $import_file = $data['uwp_import_users_file'];
553 $uploads = wp_upload_dir();
554 $csv_file_array = explode( '/', $import_file );
555 $csv_filename = end( $csv_file_array );
556 $this->path = $uploads['path'].'/'.$csv_filename;
557
558 $ext = pathinfo($csv_filename, PATHINFO_EXTENSION);
559 if (!in_array($ext, $allowed)) {
560 $response['msg'] = __( 'Invalid file type, please upload .csv file.', 'userswp' );
561 wp_send_json( $response );
562 }
563
564 $lc_all = setlocale( LC_ALL, 0 );
565 setlocale( LC_ALL, 'en_US.UTF-8' );
566 if ( ( $handle = fopen( $this->path, "r" ) ) !== false ) {
567 while ( ( $data = fgetcsv( $handle, 100000, "," ) ) !== false ) {
568 if ( ! empty( $data ) ) {
569 $file[] = $data;
570 }
571 }
572 fclose( $handle );
573 }
574 setlocale( LC_ALL, $lc_all );
575
576 $this->total_rows = ( ! empty( $file ) && count( $file ) > 1 ) ? count( $file ) - 1 : 0;
577
578 $return = $this->process_import_step();
579 $done = $this->get_import_status();
580
581 if ( $return['success'] ) {
582 $response['total']['msg'] = '';
583 if($this->total_rows > 0 && $this->imp_step == 1) {
584 $response['total']['msg'] = __( 'Total '.$this->total_rows. ' item(s) found.', 'userswp' );
585 }
586
587 $this->imp_step += 1;
588
589 $response['success'] = true;
590 $response['msg'] = '';
591
592 if ( $done >= 100 ) {
593 $this->imp_step = 'done';
594 $response['msg'] = __( 'Users import completed.', 'userswp' );
595 }
596
597 $response['data']['msg'] = ! empty( $return['msg'] ) ? $return['msg'] : $response['msg'];
598 $response['data']['step'] = $this->imp_step;
599 $response['data']['done'] = $done;
600 } else {
601 $response['msg'] = __( 'No valid data found for import.', 'userswp' );
602 }
603
604 wp_send_json( $response );
605
606 }
607
608 /**
609 * Processes import step
610 *
611 * @return array
612 *
613 */
614 public function process_import_step() {
615
616 $errors = new WP_Error();
617 if(is_null($this->path)){
618 $errors->add('no_csv_file', __('No csv file found.','userswp'));
619 }
620
621 set_time_limit(0);
622
623 $return = array('success' => false);
624
625 $rows = $this->get_csv_rows($this->imp_step, 1);
626
627 if ( ! empty( $rows ) ) {
628 foreach ( $rows as $row ) {
629 if( empty($row) ) {
630 $return['msg'] = sprintf(__('Row - %s Error: '. 'Skipped due to invalid/no data.','userswp'), $this->imp_step);
631 continue;
632 }
633
634 $username = isset($row['username']) ? $row['username'] : '';
635 $email = isset($row['email']) ? $row['email'] : '';
636 $first_name = isset($row['first_name']) ? $row['first_name'] : '';
637 $last_name = isset($row['last_name']) ? $row['last_name'] : '';
638 $bio = isset($row['bio']) ? $row['bio'] : '';
639 $display_name = isset($row['display_name']) ? $row['display_name'] : '';
640 $password = wp_generate_password();
641 $exclude = array('user_id');
642 $exclude = apply_filters('uwp_import_exclude_columns', $exclude, $row);
643
644 if(isset($row['username']) && username_exists($row['username'])){
645 $user = get_user_by('login', $row['username']);
646 $user_id = $user->ID;
647 $email = $row['email'];
648 if( !empty( $email ) && $update_existing = apply_filters('uwp_import_update_users', false, $row, $user_id) ) {
649 $args = array(
650 'ID' => $user_id,
651 'user_email' => $email,
652 'display_name' => $display_name,
653 'first_name' => $first_name,
654 'last_name' => $last_name,
655 'description' => $bio,
656 );
657 wp_update_user( $args );
658 }
659 } elseif(isset($row['email']) && email_exists($row['email'])){
660 $user = get_user_by('email', $row['email']);
661 $user_id = $user->ID;
662 } elseif((int)$row['user_id'] > 0){
663 $user = get_user_by('ID', $row['user_id']);
664 if(false === $user){
665 $userdata = array(
666 'user_login' => $row['username'],
667 'user_email' => $email,
668 'user_pass' => $password,
669 'first_name' => $first_name,
670 'last_name' => $last_name,
671 'description' => $bio,
672 'display_name'=> $display_name
673 );
674 $user_id = wp_insert_user( $userdata );
675 $notify = apply_filters('uwp_import_user_notify', 'user', $user_id);
676 wp_new_user_notification($user_id,null, $notify); //send password reset link
677 } else {
678 if( $user->user_login == $row['username'] ) { //check id passed in csv and existing username are same
679 $user_id = $row['user_id'];
680 if( !empty( $email ) && $email != $user->user_email && $update_existing = apply_filters('uwp_import_update_users', false, $row, $user_id)) {
681 $args = array(
682 'ID' => $user_id,
683 'user_email' => $email,
684 'first_name' => $first_name,
685 'last_name' => $last_name,
686 'description' => $bio,
687 'display_name' => $display_name
688 );
689 wp_update_user( $args );
690 }
691 } else {
692 $return['msg'] = sprintf(__('Row - %s Error: '. 'User could not be created.','userswp'), $this->imp_step);
693 continue;
694 }
695 }
696 } else {
697 $user_id = wp_create_user( $username, $password, $email );
698 if( !is_wp_error( $user_id ) ) {
699 $args = array(
700 'ID' => $user_id,
701 'first_name' => $first_name,
702 'last_name' => $last_name,
703 'description' => $bio,
704 'display_name' => $display_name
705 );
706 wp_update_user( $args );
707 }
708 }
709
710 if( !is_wp_error( $user_id ) ){
711 foreach ($row as $key => $value){
712 //Only write columns on the allowlist; denylist always wins.
713 if ( ! $this->is_importable_column( $key ) ) {
714 continue;
715 }
716 //Never deserialize CSV input. Cast to safe scalar string.
717 $value = $this->sanitize_import_value( $key, $value );
718 uwp_update_usermeta($user_id, $key, $value);
719 }
720 } else {
721 $return['msg'] = sprintf(__('Row - %s Error: %s','userswp'), $this->imp_step, $user_id->get_error_message());
722 continue;
723 }
724 }
725 $return['success'] = true;
726 }
727
728 return $return;
729 }
730
731 /**
732 * Returns CSV row
733 *
734 * @package userswp
735 *
736 * @param int $row
737 * @param int $count
738 *
739 * @return mixed
740 *
741 */
742 public function get_csv_rows( $row = 0, $count = 1 ) {
743
744 $lc_all = setlocale( LC_ALL, 0 ); // Fix issue of fgetcsv ignores special characters when they are at the beginning of line
745 setlocale( LC_ALL, 'en_US.UTF-8' );
746 $l = $f =0;
747 $headers = $file = array();
748 $userdata_fields = $this->get_columns();
749 if ( ( $handle = fopen( $this->path, "r" ) ) !== false ) {
750 while ( ( $line = fgetcsv( $handle, 100000, "," ) ) !== false ) {
751 // If the first line is empty, abort
752 // If another line is empty, just skip it
753 if ( empty( $line ) ) {
754 if ( $l === 0 )
755 break;
756 else
757 continue;
758 }
759
760 // If we are on the first line, the columns are the headers
761 if ( $l === 0 ) {
762 $headers = $line;
763 $l ++;
764 continue;
765 }
766
767 // only get the rows needed
768 if ( $row && $count ) {
769
770 // if we have everything we need then break;
771 if ( $l == $row + $count ) {
772 break;
773
774 // if its less than the start row then continue;
775 } elseif ( $l && $l < $row ) {
776 $l ++;
777 continue;
778
779 // if we have the count we need then break;
780 } elseif ( $f > $count ) {
781 break;
782 }
783 }
784
785 // Separate user data from meta
786 $userdata = $usermeta = array();
787 foreach ( $line as $ckey => $column ) {
788 $column_name = $headers[$ckey];
789 $column = trim( $column );
790 if ( in_array( $column_name, $userdata_fields ) ) {
791 $userdata[$column_name] = $column;
792 } else {
793 $usermeta[$column_name] = $column;
794 }
795 }
796
797 // A plugin may need to filter the data and meta
798 $userdata = apply_filters( 'uwp_import_userdata', $userdata, $usermeta );
799
800 if ( ! empty( $userdata ) ) {
801 $file[] = $userdata;
802 $f ++;
803 $l ++;
804 }
805 }
806 fclose( $handle );
807 }
808 setlocale( LC_ALL, $lc_all );
809
810 return $file;
811
812 }
813
814 /**
815 * Returns import status
816 *
817 * @return int
818 *
819 */
820 public function get_import_status() {
821 $status = 100;
822 return apply_filters( 'uwp_get_import_users_status', $status );
823 }
824
825 /**
826 * Returns import user status
827 *
828 * @return int
829 *
830 */
831 public function get_import_users_status() {
832
833 if ( $this->imp_step >= $this->total_rows ) {
834 $status = 100;
835 } else {
836 $status = ( ( 1 * $this->imp_step ) / $this->total_rows ) * 100;
837 }
838
839 return $status;
840 }
841
842 public function allowed_upload_mimes($mimes = array()) {
843 $mimes['csv'] = "text/csv";
844 return $mimes;
845 }
846
847 /**
848 * Sanitize a single CSV import value.
849 *
850 * CSV data is always a plain string. There is no legitimate reason for it
851 * to contain serialized PHP. We detect the serialization type-prefix
852 * signatures, reject them with a log entry, and return an empty string.
853 * All other values are cast to string and sanitized with sanitize_text_field().
854 *
855 * @param string $key The CSV column / meta key name.
856 * @param mixed $value The raw value from the CSV row.
857 * @return string A safe scalar string ready for DB insertion.
858 */
859 private function sanitize_import_value( $key, $value ) {
860 // Reject serialized payloads
861 if ( is_string( $value ) && preg_match( '/^[aAbBdDiIoOsScCnN][:;]/', ltrim( $value ) ) ) {
862 if ( function_exists( 'uwp_log' ) ) {
863 uwp_log( sprintf( 'Import security: serialized payload in column "%s" — discarded.', esc_attr( $key ) ) );
864 }
865 return '';
866 }
867
868 // File path columns: validate as a URL pointing inside the uploads directory only
869 if ( in_array( $key, array( 'avatar_thumb', 'banner_thumb' ), true ) ) {
870 return $this->sanitize_import_thumb( $value );
871 }
872
873 return sanitize_text_field( (string) $value );
874 }
875
876 /**
877 * Sanitizes a thumbnail path value from CSV import.
878 *
879 * Validates that the given path is a real, existing file located within
880 * the WordPress uploads directory, preventing path traversal attacks and
881 * references to arbitrary files outside the uploads directory.
882 *
883 * @param string $value Raw thumbnail path value from the CSV row.
884 * @return string Resolved absolute path if valid, empty string otherwise.
885 */
886 private function sanitize_import_thumb( $value ) {
887 $value = trim( (string) $value );
888
889 if ( empty( $value ) ) {
890 return '';
891 }
892
893 // Resolve any ../ traversal attempts before comparison
894 $real = realpath( $value );
895
896 if ( $real === false ) {
897 return ''; // Path doesn't exist on disk — reject
898 }
899
900 // Must stay within the uploads directory
901 $uploads = wp_upload_dir();
902 $base_dir = trailingslashit( realpath( $uploads['basedir'] ) );
903
904 if ( strpos( $real . DIRECTORY_SEPARATOR, $base_dir ) !== 0 ) {
905 if ( function_exists( 'uwp_log' ) ) {
906 uwp_log( sprintf(
907 'Import security: thumb path "%s" is outside uploads directory — discarded.',
908 esc_attr( $value )
909 ) );
910 }
911 return '';
912 }
913
914 // Must be an allowed image extension
915 $ext = strtolower( pathinfo( $real, PATHINFO_EXTENSION ) );
916 if ( ! in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif', 'webp' ), true ) ) {
917 return '';
918 }
919
920 return $real; // Return the resolved, canonical path
921 }
922
923 /**
924 * Return true only when the given column name is permitted for CSV import.
925 *
926 * @param string $column The CSV column / meta key name.
927 * @return bool
928 */
929 private function is_importable_column( $column ) {
930 $column = strtolower( trim( (string) $column ) );
931
932 // Denylist is checked first — it unconditionally blocks.
933 if ( in_array( $column, self::$import_meta_denylist, true ) ) {
934 return false;
935 }
936
937 return in_array( $column, self::$import_meta_allowlist, true );
938 }
939
940 /**
941 * Escape a string to be used in a CSV export.
942 *
943 * @see https://hackerone.com/reports/72785
944 *
945 * @since 1.2.3.10
946 *
947 * @param string $data Data to escape.
948 * @return string
949 */
950 public function escape_data( $data ) {
951 $escape_chars = array( '=', '+', '-', '@' );
952
953 if ( $data && in_array( substr( $data, 0, 1 ), $escape_chars, true ) ) {
954 $data = " " . $data;
955 }
956
957 return $data;
958 }
959
960 }
961 new UsersWP_Import_Export();