=== Import and export users and customers === Contributors: carazo Donate link: https://codection.com/go/donate-import-users-from-csv-with-meta/ Tags: import users, export users, csv, migrate users, bulk import Requires at least: 5.5 Tested up to: 7.1 Stable tag: 2.5.2 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Bulk import and export WordPress users and WooCommerce customers from CSV, including roles, passwords and any custom meta. == Description == **Try it out on your free dummy site: Click here => [https://demo.tastewp.com/import-users-from-csv-with-meta](https://demo.tastewp.com/import-users-from-csv-with-meta)** Import, export and migrate WordPress users and WooCommerce customers from a CSV file. Bulk-create or update thousands of users in seconds, schedule automatic recurring imports from a local file or URL, and carry over roles, passwords and any custom meta field. Works with WooCommerce, BuddyPress, Advanced Custom Fields, Paid Membership Pro, WooCommerce Memberships, WooCommerce Subscriptions and many more. * Import CSV file with users directly to your WordPress or customers into WooCommerce * Import thousands of users or customers in only some seconds * Export users or customers to a CSV file, filtering by role or registered date * You can also import meta-data like data from WooCommerce customers * You can assign roles while importing. * Send a mail to every new user, this mails can be saved as templates and are fully customizable, before sending you can test it * You can also update users if the user is already in your WordPress * Create a cron task to import users periodically * Edit the metadata (you will be able to edit the metadata imported using metakeys directly in the profile of each user) * Extend the plugin using the hooks we provide * Compatible with WPML [read the documentation](https://wpml.org/documentation/plugins-compatibility/import-users-from-csv-with-meta-and-wpml/) to see how you can translate the front-end import and export users page and send translated email notifications to users Moreover this plugin is compatible with many other plugins to be able to import and include them data, subscriptions, memberships, etc. Take a look: * WooCommerce: to import the customer data * WooCommerce Memberships: to import memberships * WooCommerce Subscriptions: to create subscriptions associated with users while they are being imported * BuddyPress: to import custom BuddyPress avatars, fields, groups and roles * Advanced Custom Fields: to import data to the fields you define there * Paid Membership Pro: to import memberships, included compatibility with PMPro version 3 * Indeed Ultimate Membership Pro: to import memberships * Paid Member Subscriptions: to import memberships * Allow Multiple Accounts: plugin will allow the same rules importing than this plugin * Groups: to assign users to groups while importing * New User Approve: you can import users and approbe/wait for approve them * Users Group: to assign users to groups while importing * WP LMS Course: to enroll users in the courses while importing * WP Members: to import memberships * WP Users Group: to assign users to groups while importing * WooCommerce Membership by RightPress: to create memberships while users are being imported * WP Private Content Plus: To import and export the groups to which users are assigned If you have some problem or doubt: * Read our documentation * Ask anything in support forum, we try to give the best support ### **Common use cases** * Migrate users from one WordPress site to another * Bulk create or update WooCommerce customers from a spreadsheet * Sync users automatically from an external CRM or ERP via CSV * Onboard members to a membership site (Paid Membership Pro, WooCommerce Memberships) * Periodically import subscribers or students from an external list * Import users from the frontend using a shortcode ### **Usage** Once the plugin is installed you can use it. Go to Tools menu and there, there will be a section called _Insert users from CSV_. Just choose your CSV file and go! ### **CSV generation** You can generate CSV file with all users inside it, using a standar spreadsheet software like: Microsoft Excel, LibreOffice Calc, OpenOffice Calc or Gnumeric. You have to create the file filled with information (or take it from another database) and you will only have to choose CSV file when you "Save as..." the file. As example, a CSV file is included with the plugin. ### **Some considerations** Plugin will automatically detect: * Charset and set it to **UTF-8** to prevent problems with non-ASCII characters. * It also will **auto detect line-ending** to prevent problems with different OS. * Finally, it will **detect the delimiter** being used in CSV file == Frequently Asked Questions == = I get a "too many redirects" error on my site, how can I solve it? = This is caused by the option that forces the users to reset their password. When it is used, each of those users is redirected to their password page on every page load until they change it, and if that destination cannot be reached (for example, because wp-admin is blocked for customers or subscribers by WooCommerce, by a membership plugin or by a security plugin) the browser ends up in a redirection loop. Since version 2.4.12 the plugin detects this situation and gives up after 3 consecutive redirects, so the site stays usable, but you can also clear the flag for the users that are already affected: go to the plugin documentation tab and use the button that removes the force reset password metadata from all the users. If you want to change how many redirects are allowed before the plugin gives up, use the `acui_force_reset_password_max_redirects` filter (return 0 to disable the limit): `add_filter( 'acui_force_reset_password_max_redirects', function( $max ){ return 5; } );` = Which page are the users sent to when I force them to reset their password? = By default they are sent to their WordPress profile page. If WooCommerce or WP User Manager are active, they are sent to the password section of the account page of those plugins instead. You can point them anywhere else using the `acui_force_reset_password_edit_profile_url` filter, and you can add your own exceptions (pages where the redirection must not happen) using `acui_force_reset_password_redirect_condition`. == Screenshots == 1. Import dialog 2. Recurring import dialog 3. Export dialog 4. File sample 5. Extra profile information (user meta) == Changelog == = 2.5.2 = * Security fix (privilege escalation): the role-assignment guards in the importer honoured a `caller_can_promote_users` value taken straight from the submitted form data instead of checking the current user's capability. That value is only legitimate on the Action Scheduler cron path, where it is derived from a capability at schedule time; on every request-driven import path (the manual backend importer and the AJAX batch import action the plugin UI uses) the form data is the raw request, so a user with only `create_users` could add `caller_can_promote_users=1` to the request themselves and get the `administrator` role applied, including to their own account. The pre-recorded decision is now only trusted on the cron path; every request-driven path re-derives it from the current user's own capabilities (reported by yzx001) * Security fix (privilege escalation): the front-end shortcode importer's "change role of users that are not present in the CSV" feature read the `acui_frontend_change_role_not_present` / `acui_frontend_change_role_not_present_role` options with no capability check of its own, and those options could be set by anyone able to reach the settings screen, which is gated only on `create_users`. A user with only `create_users` could point the option at `administrator` and then run the front-end importer to promote every existing editable-role user omitted from the CSV to administrator. Saving those two options now requires `promote_users`, matching the equivalent Cron tab option, and the front-end import path now also requires `promote_users` before honouring the option at run time (reported by BINESH MADHARAPU) = 2.5.1 = * Fixed the `[export-users]` shortcode dropping the `columns` attribute when it used the `key=>Label` renaming syntax (for example `columns="user_email=>Email,first_name=>First name"`), which caused all columns to be exported instead of just the selected ones. The `=` and `>` characters were not part of the shortcode attribute's whitelist and made it get sanitized down to an empty value = 2.5 = * Security hardening: exporting users is now gated by its own `acui_export_capability` filter (default `edit_users`) instead of sharing the `acui_capability` filter used by import. Previously, any site that lowered `acui_capability` to give a role import-only access (for example, to `add_users`) was also handing that same role access to the Export tab and its download/save endpoints, since both checks resolved to the exact same filter call with no way to tell them apart. The Export tab, its nav link, the plugin list "Export" action link and the export AJAX/download handlers now check the new filter independently, so an import-only role stays import-only unless the new filter is explicitly overridden * The "Update existing users?" section on the Import tab now hides the "Update emails?", "Update roles for existing users?" and "Update passwords for existing users?" fields when "Update existing users?" is set to "No", since they have no effect in that case = 2.4.18 = * Security fix (privilege escalation): the CSV exporter writes fields with the NUL character as the escape character (`fputcsv(..., '"', "\0")`), but the importer's `SplFileObject::fgetcsv()` calls were passing only the delimiter, which silently kept PHP's default backslash escape character. Any registered user able to save their own profile could set `display_name` to a value ending in a comma and a backslash (e.g. `a,\`) and `nickname` to a value starting and ending with a comma around a role name (e.g. `,administrator,q`). When an administrator later used the plugin's own Export tab and re-imported that file with "Update existing users" and "Update roles for existing users" both set to "yes" — the plugin's documented backup/migration workflow — the escape mismatch merged the `display_name` cell into the `role` column and split the `nickname` cell, so the parsed role column for the attacker's own row read exactly `administrator`, and the administrator's own authorized import call applied it. The importer's `fgetcsv()` calls now use the same explicit escape character as the exporter, so no combination of profile field values can shift the parsed column boundaries * Security fix (hardening): as defense in depth for the same code path, assigning the `administrator` role during an import now additionally requires the `create_users` capability (not only `promote_users`) on the account running the import. This does not change behavior on sites using the plugin's own default access gate, but stops a future column-shift bug — or a site that has lowered the `acui_capability` filter to allow non-administrators to run imports — from being able to deliver an `administrator` role assignment through a CSV. Other roles present on the same row are unaffected; only `administrator` is dropped (with a warning) when `create_users` is missing = 2.4.17 = * Security fix (privilege escalation): the 2.4.16 fix only closed the literal `acui_fire_cron_no_session` no-session action; it did not close the same gap on a genuinely unattended run of the recurring import (an Action Scheduler queue tick, with no `$caller_can_promote_users` argument at all). A user with only `create_users` could leave the Cron tab's default role empty (so the `promote_users` gate on that field was never tripped), point the recurring import at an attacker-controlled CSV with its own `role` column set to `administrator`, and activate it. On the next unattended tick, the execution user still falls back to the site's first Administrator, and the per-row role check evaluated that substituted Administrator's own capability rather than the capability of whoever had actually configured the task, so the CSV-supplied `administrator` role was applied. Whether an unattended cron run may apply any role, including one carried in the CSV's own role column, is now decided by the `promote_users` capability of the user who last saved the Cron tab settings, recorded at save time, instead of being re-derived from the (possibly substituted) execution user at run time * Security fix (privilege escalation): the manual backend import ("Import" tab) only required `create_users` to run at all, but let the request also enable "delete users not present" and "change role of users not present" without holding `delete_users` or `promote_users` respectively — the same gap that the Cron tab already had fixed in 2.4.15. Both options on the manual import now also require the matching capability = 2.4.16 = * Security fix (privilege escalation): the 2.4.15 fix gated the Cron tab's stored default role and execution user, but not the "Run now" no-session action (`acui_fire_cron_no_session`) itself, nor the role that a CSV row can carry in its own role column. A user with only `create_users` could still save an attacker-controlled file path (not gated by `promote_users`) and trigger that no-session run, which clears the current user and falls back to the site's first Administrator; the per-row role check then saw that substituted Administrator's capabilities instead of the actual caller's, so a role of `administrator` delivered through the CSV itself (rather than the stored default) was still accepted. The role assignment check on every cron-triggered run, including multi-step batches, now uses the capability of the user who actually triggered the run instead of the capability of the substituted execution user = 2.4.15 = * Security fix (privilege escalation): saving the Cron tab settings (`acui_cron_save_settings`) checked only the shared plugin nonce and, for the "User that runs the cron" field, that the selected user held the same broad menu capability (`create_users` by default) as whoever was saving. This let a user with `create_users` but not `promote_users` (or `delete_users`) select an Administrator as the execution identity, set the default role to `administrator`, or enable "delete users not present"/"change role of users not present", and then trigger the "Run now" no-session action, which runs the import with no logged-in user and falls back to the site's first Administrator. The `promote_users` check on role assignment and the missing capability checks on user deletion and role change only saw that substituted Administrator, not the actual caller, so a delegated importer could create a new Administrator account or delete/demote existing users. The Cron tab now requires `promote_users` to set a default role, to set the "change role of users not present" role, or to select an execution user that itself has `promote_users`, and requires `delete_users` to enable "delete users not present" * Security fix (hardening): the same settings are also now rejected outright (instead of silently ignored) when the current user lacks the required capability, so a misconfigured attempt fails loudly rather than saving a partial, inconsistent state = 2.4.14 = * Improvement: the "Path or URL of file that is going to be imported" field (manual import and Cron tab) now explains right on screen that a local path must be located inside the WordPress uploads folder, and shows the actual folder path of the site * Improvement: added a "Check path" button next to that field to verify, before saving or running the import, whether the entered path or URL will be accepted, and why not otherwise * Improvement: saving the Cron settings now shows a warning notice when the configured path is not a valid local path (wrong extension, file not found, or outside the uploads folder), instead of only failing silently at the next scheduled run * New: added the `acui_allowed_local_csv_base_dirs` filter so a developer can whitelist extra local base folders for the CSV path (e.g. a folder an external process writes to), without disabling the underlying security check added in 2.4.3 * Security fix (hardening): importing a CSV from a remote URL (manual import and Cron tab) now blocks link-local (`169.254.0.0/16`, including the cloud metadata endpoint `169.254.169.254`) and carrier-grade NAT (`100.64.0.0/10`, `198.18.0.0/15`) hosts, and re-validates every redirect hop against the same SSRF blocklist instead of letting `download_url()` follow redirects unchecked. This closes the same class of Server-Side Request Forgery that was already fixed for the BuddyPress/BuddyBoss `bp_avatar` import in 2.4.3/3.x, but that fix had not been applied to the main CSV-by-URL import path = 2.4.13 = * Improvement: the role errors during the import now say exactly what is wrong instead of always blaming permissions. The row is checked role by role and the message tells which single role failed and why: the role does not exist (and lists the roles that can be used), the role exists but the current user is not allowed to assign it (and lists which ones they can), or the role column is empty on that row. Before, a role that simply did not exist was reported as *"You do not have permission to assign some of the next roles"* whenever the "update roles of existing users" option was set to "no", because the "role does not exist" check was skipped in that case, and both messages printed the whole list of roles of the row instead of the offending ones * Improvement: when the CSV carries the name of the role (`Conference`) instead of its slug (`conference_member`), the error now points to the right slug instead of only saying that the role does not exist * Fix: roles whose slug contains uppercase letters (created that way by some role editor plugins) can be imported again. The values of the `role` column were lowercased but compared against the role slugs as they are stored, so those roles were always rejected as non existing or invalid * Fix: `ACUI_Helper::get_editable_roles()` uses `wp_roles()` instead of reading the `$wp_roles` global directly, which could still be uninitialized in cron or front end imports, leaving the list of editable roles empty and making every role of the CSV fail * Fix: a `role` cell containing the `::` list separator no longer produces a fatal error when the row is prepared = 2.4.12 = * Fix: the "force users to reset their password" option no longer produces "too many redirects" errors. The redirection ran on every front end page load and only stopped when the add-on condition matched exactly, so any setup where the destination could not be reached or recognized (wp-admin blocked for customers by WooCommerce or a membership plugin, a My account URL that did not match the `home_url()` string comparison because of trailing slashes, www, https or a language prefix, an account page built with blocks or a page builder instead of the shortcode...) sent the user back and forth forever. The redirection is now skipped when the user is already on the destination URL, it does not run on AJAX, REST, cron, XML-RPC or non GET requests, and a counter stops it after 3 consecutive redirects (filterable through the new `acui_force_reset_password_max_redirects` hook) so the worst case is that the password change is not enforced instead of the site being unreachable = 2.4.11 = * Security fix: the mail options save handler no longer writes to arbitrary posts. The `template_id` form field was passed straight to `wp_update_post()` without checking the post type or `current_user_can( 'edit_post' )`, so a user with only the `create_users` capability could overwrite the title and the content of any post or page of the site (and inject arbitrary CSS through the `