Export ACF Google Map Fields to CSV or XML

Export ACF Google Map Fields to CSV or XML

Google Map fields in Advanced Custom Fields are automatically detected by WP All Export and added to the ACF section in Available Data.

The ACF Google Map field stores an address, that is then rendered into a map on the frontend. For example:

When exported, this field generates the following export columns:

_address: Exports a string of text with the inserted address. Example: 123, Foo Bar Street, Melbourne, Victoria, 3000, Australia._lat: Exports the latitude of the inserted address. Example: -37.8156542._lng: Exports the longitude of the inserted address. Example: 144.9608007.

Here's how it looks in an export file generated by WP All Export:

Calling PHP Functions In Your Import Configuration

Calling PHP Functions In Your Import Configuration

You can execute any PHP function (native functions, or user-defined functions) from inside virtually any text box (post title, post content, Custom Field values, etc.) of WP All Import. You can even pass values from your data file to the PHP function.

Example 1 – using the native str_replace function to remove commas

Here, we』re using the str_replace function to remove commas from the post title:

[str_replace(",", "", {title[1]})]

string replace example

Note the use of the double quotes instead of single quotes. You must use double quotes when executing PHP functions from within WP All Import. You can』t use single quotes.

Example 2 – custom code in the Function Editor

WP All Import has a built-in code editor where you can write your own custom functions. You can access the Function Editor when editing your import template, or via All Import > Settings in your WordPress dashboard.

The Function Editor makes code editing easy:

Syntax highlightingBuilt-in linting to prevent site-breaking errorsCode only runs during an importWrite and test code on the edit import page using the 「Preview」 feature

As an example, let』s say we are importing WooCommerce products and need to increase the price by 50%, and round it so that it looks nice.

We can do this by writing a custom PHP function to process and return the desired price.

Here』s an example of a function that you could use:

function round_price( $price = null, $multiplier = 1, $nearest = .01, $minus = 0 ) {if ( !empty( $price ) ) {// strip any extra characters from price$price = preg_replace("/[^0-9,.]/", "", $price);// perform calculationsreturn ( round ( ( $price * $multiplier ) / $nearest ) * $nearest ) - $minus; }}

Which can be added directly to the Function Editor on the Edit Import page:

Function Editor Example

Then we call this function in the 「Regular Price」 field provided by our WooCommerce Add-On, where we would customize it based on our needs. We want to set the multiplier to 1.5 so our prices are increased by 50%. We want set the round parameter to 10 so prices will be rounded off to the nearest $10. And finally, we want to set the minus parameter to .01, so we get prices ending in $.99.

[round_price({price[1]},"1.5","10",".01")]

This way, a product with a price of $55 will be imported with a price of $79.99.

Function in import template

You can set the parameters to be whatever you like, or modify the code itself to do something else. Writing code in the Function Editor is easy, fast, and infinitely customizable. Once you click Save Functions you can click the Preview button to see the results, and then modify as needed.

Related

IF Statements

FOREACH Loops

How to Pass Exported WordPress Data Through PHP Functions

How to Pass Exported WordPress Data Through PHP Functions

You can use PHP functions to process your WordPress exports in WP All Export with the following steps:

Add or drag in the export fieldClick the field to edit it.Save your function in the Function Editor.Enable "Export the value returned by a PHP function" and type in the function name.Save the field.

Use PHP to process WordPress data while it's exported

WP All Export has the ability to process the data in any export field with PHP functions. You can write a custom function to process your data, or use a native PHP function to do it.

First, click the Add Field button under the export template and then select the field from the Select a field to export drop-down list. You can also click an existing field to bring up the edit export field modal.

From the edit export field modal, enable Export the value returned by a PHP function:

Export the value returned by a PHP function option.

In the example above, the Title is being exported. In this case, the title will be passed to your function as the first and only argument:

function my_process_title( $title ) {
// do something with $title
// return it
}

Using native PHP functions on export fields

In the your_function_name text box, you can use any native PHP function that uses one argument. For example, if you wanted to make the Title export field export all of the titles in uppercase, you could use strtoupper():

In fact, you can use any function that exists and is accessible during the export. That includes WordPress functions like wp_get_attachment_url, sanitize_title, etc.

Using custom PHP functions on export fields

For more complicated cases, you can write full-featured PHP functions inside the Function Editor.

As an example, let's say that you're exporting data from a real estate theme like WP Residence and you want to export the property gallery image URLs for each exported property. They're stored in the image_to_attach custom field as IDs. If we were just to export that custom field, we'd get this:

35,39,37,41,43,

We could use this function to extract the IDs, get the attachment URLs, then return a delimited list of the URLs:

function my_convert_ids_to_urls( $ids ) {
// Return nothing if there are no IDs.
if ( empty( $ids ) ) {
return null;
}

// Turn the IDs into an array of IDs.
$ids = explode( ',', $ids );

// Convert the IDs to attachment URLs.
$urls = array_map( function( $id ) {
return wp_get_attachment_url( $id );
}, $ids );

// Get rid of empty array elements.
$urls = array_filter( $urls );

// Return comma-delimited list of image URLs.
return implode( ',', $urls );
}

Let's use this function with the image_to_attach export field from WP Residence:

Using image on the image_to_attach field.

And that's it. Our export file will contain a column labeled Image Gallery URLs, and each row in that column will contain a comma delimited list of image URLs for the images in that property gallery.

Head over to https://www.wpallimport.com/documentation/developers/code-snippets/ for a full list of code snippets to get you started.

Export ACF Media Fields to CSV or XML

Export ACF Media Fields to CSV or XML

ACF fields are automatically detected by WP All Export. To customize your export file, select fields from Available Data › ACF and drag and drop them into the export editor.

The following ACF media fields can be exported: Image, File, Wysiwyg Editor, oEmbed, and Gallery. Here's more information on each field:

Image: Exports the URL to the image. File: Exports the URL to the file. Wysiwyg Editor: Exports the text added to this editor field, exports as plain text.oEmbed: This field provides an interactive component for embedding videos, images, tweets, audio, and other content; it exports an URL to the embedded resource.Gallery: Exports the URLs to the images within the gallery field.

WP All Export treats all ACF media fields in the same way. Image fields export the URL to the image or file. Multiple images or files in a single field will be separated by the pipe ( | ) character.

Using PHP to Extend WP All Import's Functionality

Using PHP to Extend WP All Import's Functionality

Using custom PHP code, you can modify the values in your file to fit the requirements of WooCommerce or other plugins. Use FOREACH loops to retrieve and modify multiple elements in your file. Select only needed file records using custom XPath. And, extend WP All Import using our filters and actions.

Related

IF Statements

FOREACH Loops

Filters & Actions

Custom XPath

Example Code To Simplify Your Imports and Exports

Example Code To Simplify Your Imports and Exports

These snippets cover the more common cases where custom code is necessary during import. Unique plugin or theme requirements often necessitate such code. It can also be due to odd import file formats that aren't fully compatible with WP All Import on their own. You would place these snippets in WP All Import's Function Editor.

Click Here for more information.

Delete a Custom Field If Another Field Has a Specific Value

Here we use the pmxi_saved_post action, get_post_meta(), and delete_post_meta() to delete _my_custom_field if _my_update_check is set to 'yes'.

function conditional_delete( $id ) {

// Retrieve check field's value.
$check = get_post_meta( $id, '_my_delete_check', true );

if ( $check === 'yes' ) {

// If check value is 'yes' delete _my_field.
delete_post_meta( $id, '_my_field' );

}
}
add_action( 'pmxi_saved_post', 'conditional_delete', 10, 1 );

Markup Price with Minimum and Maximum Limits

Here we clean up the price in our file, add a markup, and ensure it's within our required minimum and maximum prices. The $price and $multiplier parameters are required. The others are optional and can be used to further customize the final price:

ParameterDescription$priceThe original price.$multiplierMultiply the original price by this number.$nearestRound to the nearest value (0.01 by default).$minusSubtract this value from the rounded price.$mapThe minimum price that can be returned.$msrpThe maximum price that can be returned.

We call the function from any field that needs a marked up price:

function round_price_with_limits( $price = null, $multiplier = 1, $nearest = .01, $minus = 0, $map = 0, $msrp = 9999999999) {

// Ensure a price was provided.
if ( !empty( $price ) ) {

// Remove unwanted characters from price.
$price = preg_replace("/[^0-9,.]/", "", $price);

// Remove unwanted characters from min price.
$map = preg_replace("/[^0-9,.]/", "", $map);

// Remove unwanted characters from max price.
$msrp = preg_replace("/[^0-9,.]/", "", $msrp);

// Calculate price with markup and round it.
$rounded_price = ( round ( ( $price * $multiplier ) / $nearest ) * $nearest ) - $minus;

// If price is less than minimum, return minimum.
if($rounded_price $msrp){

return $msrp;

} else {

// Return price otherwise.
return $rounded_price;

}

}
}

Trigger the next Import on Completion of an Import

Here we use the pmxi_after_xml_import action to redirect the connection to the next import's 'trigger' URL. We are able to ensure the first import will always complete before the next is started. This is especially helpful when our second import depends on data provided by the first.

All imports still require the 'processing' cron jobs or they won't run.

function after_xml_import($import_id, $import)
{

// Only run for import ID 5.
if ($import_id == 5) {

// Call the next import's trigger URL.
wp_remote_get("yourtriggerURLhere");

}
}

add_action('pmxi_after_xml_import', 'after_xml_import', 10, 2);

Reduce Stock for Imported Order Items

Here we use the pmxi_saved_post action and wc_reduce_stock_levels() to deduct ordered items from product stock. This is necessary since WP All Import doesn't touch product stock when importing Orders by default.

function decrease_order_item_stock($post_id, $xml_node, $is_update)
{

    // Retrieve the import ID. 
$import_id = ( isset( $_GET['id'] ) ? $_GET['id'] : ( isset( $_GET['import_id'] ) ? $_GET['import_id'] : 'new' ) );

// Only run for imports 899, 898, and 895.
if ( in_array( $import_id, [899, 898, 895] ) ) {

// Decrease stock for order items if it hasn't already
// been decreased.
wc_reduce_stock_levels( $post_id );

     }
}

add_action('pmxi_saved_post', 'decrease_order_item_stock', 10, 3);

Import Multisite Users to Multiple Subsites

Here we use our Custom Fields capability to assign Users to more than one subsite on WordPress Multisite. This workaround is required due to the way Multisite stores Users.

Custom fields are used to identify which subsite each User is authorized to use. Our example Multisite install uses the prefixes below for each subsite:

wp_ wp_2_ wp_3_

A user must have these three Custom Fields defined to be authorized on all of our subsites:

wp_capabilities wp_2_capabilities wp_3_capabilities

Each field must contain a serialized array of User Roles:

a:1:{s:10:"subscriber";b:1;}

For example:

Get Parent Attributes When Exporting Variations to Google Merchant Center

Here we use wc_get_product to retrieve the WC_Product object for each exported record. This lets us retrieve attributes that are directly assigned to the parent products. By default, when exporting to GMC only the attributes attached to the variations themselves are exported.

// $attr must be the name of the attribute to be retrieved.
function my_get_parent_attr( $id, $attr ){

// Get product object.
$prod = wc_get_product( $id );

// Check product type.
if( $prod->is_type('variation') ){

// Retrieve parent product's ID.
$parent = wc_get_product( $prod->get_parent_id() );

// Return parent's attribute value.
return $parent->get_attribute($attr);

}else{

// Return attribute value for simple products.
return $prod->get_attribute($attr);

}
}

Here it is in use:

Enable Rapid Add-On API Sections for User or Customer Import

Here we use the pmxi_visible_template_sections filter to display our add-ons section. This allows it to show up for Users and Customers. We use the wp_all_import_is_images_section_enabled filter to hide the Images section as it's not normally displayed for Users and Customers.

// Enable our custom add-on's section.
function show_addon_section_users_customers( $sections, $post_type ) {

// Enable add-on section for Users.
if ( 'import_users' == $post_type )
$sections[] = 'featured';

// Enable add-on section for Customers.
if ( 'shop_customer' == $post_type )
$sections[] = 'featured';

return $sections;
}

add_filter( 'pmxi_visible_template_sections', 'show_addon_section_users_customers', 11, 2 );

// Disable the Images section.
function hide_images_users_customers( $is_enabled, $post_type ){

// Disable Images section for Users, return true to enable.
if ( 'import_users' == $post_type )
$is_enabled = false;

// Disable Images section for Customers, return true to enable.
if ( 'shop_customer' == $post_type )
$is_enabled = false;

return $is_enabled;

}

add_filter( 'wp_all_import_is_images_section_enabled', 'hide_images_users_customers', 10, 2 );

And, here's what the run() function would look like:

$wp_user_avatar_addon->run(
array(
"post_types" => array( "import_users", "shop_customer" )
)
);

Export Total Number of Sales for Variations

Here we export the total sales for each of our variations using some custom code. We need this workaround because WooCommerce doesn't track total sales at the variation level.

We call it like this:

function my_get_total_sales( $id ) {

// Only use database calls for variation counts.
if ( get_post_type( $id ) === 'product_variation' ) {
global $wpdb;
$table = $wpdb->prefix . 'woocommerce_order_itemmeta';
$count = array();
$total_count = 0;
$itemmeta = $wpdb->get_results( "SELECT `order_item_id` FROM `$table` WHERE `meta_key` = '_variation_id' AND `meta_value` = '$id'" );

// Ensure item meta was returned.
if ( ! empty( $itemmeta ) ) {

// Process each item.
foreach ( $itemmeta as $result ) {

// Ensure the order quantity was retrieved.
if ( $qty = $wpdb->get_row( "SELECT `meta_value` FROM `$table` WHERE `meta_key` = '_qty' AND `order_item_id` = '{$result->order_item_id}'" ) ) {

// Save the quantity ordered.
$count[ $result->order_item_id ] = $qty->meta_value;

}
}

// Switch to order items table.
$table = $wpdb->prefix . 'woocommerce_order_items';

// Process each item meta record.
foreach ( $itemmeta as $item_obj ) {

// Retrieve Order ID for each ordered item.
$order_id_results = $wpdb->get_row( "SELECT `order_id`,`order_item_id` FROM `$table` WHERE `order_item_id` = '{$item_obj->order_item_id}'" );

// Only continue if order was returned.
if ( ! empty( $order_id_results ) ) {

// Retrieve the order status.
$status = get_post_status( $order_id_results->order_id );

// Check if order is completed.
if ( $status == 'wc-completed' ) {

// If it was add that item's count to total.
$total_count = $total_count + $count[ $item_obj->order_item_id ];

}
}
}
}

// Return total sales for variation.
return $total_count;

} else {

// Get product object.
$product = wc_get_product( $id );

// If the product isn't varible, return total sales.
return $product->get_total_sales();

}
}

Workaround for Importing from FTP

Here we call a custom function in the 'Download from URL' field to import from FTP. The code must be placed outside of WP All Import's Function Editor - your theme's functions.php file is a good place. We make use of wp_upload_dir() when saving the retrieved file.

The file is saved in the uploads directory, it's up to you to take any security precautions you deem necessary.

We call the function from the 'Download from URL' option on Step 1:

If your FTP requires a username and password, it would look something like this:

[custom_file_download("ftp://username:[email protected]/full/path/to/file.csv","csv")]

Otherwise, you can omit that part:

[custom_file_download("http://example.com/full/path/to/file.csv","csv")]

function custom_file_download( $url, $type = 'xml' ) {

// Set our default cURL options.
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "GET" );

/* Optional: Set headers if needed.
* $headers = array();
* $headers[] = "Accept-Language: de";
* curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
*/

// Retrieve file from $url.
$result = curl_exec( $ch );

// Return error if cURL fails.
if ( curl_errno( $ch ) ) {
exit( 'Error:' . curl_error( $ch ) );
}
curl_close( $ch );

// Identify the upload directory path.
$uploads = wp_upload_dir();

// Generate full file path and set extension to $type.
$filename = $uploads['basedir'] . '/' . strtok( basename( $url ), "?" ) . '.' . $type;

// If the file exists locally, mark it for deletion.
if ( file_exists( $filename ) ) {
@unlink( $filename );
}

// Save the new file retrieved from FTP.
file_put_contents( $filename, $result );

// Return the URL to the newly created file.
return str_replace( $uploads['basedir'], $uploads['baseurl'], $filename );

}

Variations Not Updating When Selected on Frontend

Here we use the woocommerce_ajax_variation_threshold filter to enable 150 variations to be loaded via AJAX on the frontend. This enables the product options to update when selecting each attribute from the dropdown. By default, those updates only work for products with 30 or fewer variations and products with more only have the selected options validated when adding them to the cart.

function soflyy_change_threshold( $amount, $product ) {

// The max number of variations to load via AJAX.
return 150;

}

add_filter( 'woocommerce_ajax_variation_threshold', 'soflyy_change_threshold', 10, 2 );

Retrieve Post Type of Current Import

Here we use the wp_all_import_is_post_to_create filter to retrieve the Post Type being imported. This code works with any of our hooks.

function get_import_post_type($continue_import, $current_xml_node, $import_id){

// Retrieve import object.
$import = new PMXI_Import_Record();
$import->getById($import_id);

// Ensure import object is valid.
if ( ! $import->isEmpty() ) {

// Retrieve post type.
$post_type = $import->options['custom_type'];

// Use post type in your code.

}
}

add_filter('wp_all_import_is_post_to_create', 'get_import_post_type', 10, 3);

Houzez - Linking Neighborhoods, Cities, States, and Countries

Here we use the pmxi_saved_post action to link Neighborhoods, Cities, and States for the Houzez theme. This is necessary because the Houzez Add-On for WP All Import doesn't handle it currently. You must use a Taxonomies import for these examples.

The neighborhoods are linked to cities by entries in the Options table. You'll need to set a 'parent_city' Custom Field:

Then add the code below to the Function Editor:

function link_houzez_city( $id, $xml, $is_update ) {

// Retrieve 'parent_city' and use it to find that City.
$term = get_term_by( "name", get_term_meta($id, "parent_city", true), "property_city" );

// Retrieve the City's slug.
$slug = $term->slug;

// Generate the appropriate Option name.
$option_name = '_houzez_property_area_' . $id;

// Set the option to link this Neighborhood to its City.
update_option($option_name, array("parent_city"=>$slug));

}
add_action( 'pmxi_saved_post', 'link_houzez_city', 10, 3 );

When adding Cities to States you must set the 'parent_state' Custom Field ( instead of parent_city ):

function link_houzez_state( $id, $xml, $is_update ) {

// Retrieve 'parent_state' and use it to locate that State.
$term = get_term_by( "name", get_term_meta($id, "parent_state", true), "property_state" );

// Retrieve the State's slug.
$slug = $term->slug;

// Generate the Option name.
$option_name = '_houzez_property_city_' . $id;

// Set the Option to link the City and State.
update_option($option_name, array("parent_state"=>$slug));

}

add_action( 'pmxi_saved_post', 'link_houzez_state', 10, 3 );

When adding States to Countries the Custom Field must be named 'parent_country' and must contain the 2 character Country Code ( e.g. US, CA, GB, IE, etc )

function link_houzez_country( $id, $xml, $is_update ) {

// Retrieve 'parent_country' and use it as the slug.
$slug = get_term_meta($id, "parent_country",
true);

// Generate the Option name.
$option_name = '_houzez_property_state_' . $id;

// Set the Option to link State and Country.
update_option($option_name,
array("parent_country"=>$slug));

}

add_action( 'pmxi_saved_post', 'link_houzez_country',
10,
3 );

Flatsome Theme - Taxonomy Top and Bottom Content

Here we use a custom function to import data to the Top and Bottom Content fields of the Flatsome Theme. This is necessary due to the format required for those fields.

The bottom content and top content are stored in a serialized array in the 'cat_meta' field. You must tell WPAI to update that field ( Manage Imports > Import Settings ):

Here's an example snippet to use in the Function Editor that accepts your Bottom and Top Content values as parameters. It returns the serialized array as required by Flatsome:

function my_term_meta( $bottomContent, $topContent = '' )
{

$meta = array('cat_header' => $topContent, 'cat_footer' => $bottomContent);

return serialize($meta);

}

Here's the code in action:

Add Entry to the Import Log

Here we are using the pmxi_saved_post action to add an entry to the import log every time a post is successfully imported or updated. You can use a different action to add log entries at a different time, such as whenever an image is imported, or a custom field updated, etc.

function my_custom_log_entry($id) {
$logger = function($m) {printf("[%s] $m", date("H:i:s"));flush();};

// Define your log entry here
    call_user_func($logger, "This is my log message.");

}

add_action('pmxi_saved_post', 'my_custom_log_entry', 10, 1);

Reference Taxonomy Terms by Custom ID

Here we use get_terms() to retrieve previously imported categories by the _my_old_id custom field. This is necessary when using import files that only reference categories by ID.

Here's an example category file:

namedescriptionid_parentid_categoryParentAn example parent category.1ChildAn example child category.12

Here's an example product file:

namepriceid_category_defaultApples1.252

We start with a Taxonomies > Product Categories import. On Step 3, we save our ID to a custom field named _my_old_id:

The code below is placed in the Function Editor:

function my_get_cat_slug( $old_id, $id = true )
{
$args = array(
'hide_empty' => false,

// Specify what field to search for the ID.
'meta_query' => array(
array(
'key' => '_my_old_id',
'value' => $old_id,
'compare' => '='
)
)
);

// Search the Product Category taxonomy for our term.
$terms = get_terms( 'product_cat', $args );

if( $id === true ){

// By default we return the term's WordPress ID.
return $terms[0]->term_id;

}else{

// If $id is false we return the term's slug instead.
return $terms[0]->slug;

}
}

Here we use our code to specify the Parent Term:

If all Parent Terms are listed before their children in the file, the records per iteration can be set to 1 to ensure they're linked (Manage Imports > Import Settings):

Otherwise, the import must be run twice to link the Parent Terms.

Now the imported Product Category terms are available to use when importing our products. We need each term's slug so we set the second parameter to false when calling our code:

If you need to reference multiple categories per record, you'll need some additional code in the Function Editor:

function my_get_multiple_slugs( $old_id, $id = true, $delimiter = ',' ){

// Split the category references on the given delimiter.
$values = explode($delimiter, $old_id);
// Declare a variable for our located categories.
$cat_list = [];

// Process each category reference.
foreach( $values as $value ){
// Call our other function to process each category.
$cat_list[] = my_get_cat_slug( $value, $id );
}

// Return a list of the located categories using the defined delimiter.
return implode($delimiter, $cat_list);
}

The taxonomies section should be updated to something like below, making sure the delimiter provided matches that used in your file:

Find File Elements That Start with Certain Text

Here we use an XPath expression to return values for all elements in the file that start with 'image'. The values are returned separated by commas.

{./*[starts-with(local-name(), 'image')]}

Map File Values During Import

Here we use a custom function to map an amenity code in our file to its description. This allows us to match our imported values with those on our site. The code expects either a single amenity or a comma-separated string of values.

function soflyy_map_amenities($amenities)
{

// Convert comma separated list to array.
$amenities = explode(",",$amenities);

// Define your mappings.
$map_values = array(
'BA' => 'Balcony',
'SP' => 'Shared Pool',
'SS' => 'Shared Spa',
'file value' => 'returned value');

// Declare a new array for our mapped values.
$mapped_amenities = array();

// Process each amenity.
foreach($amenities as $amenity)
{

// Check if we have a mapping for this amenity.
if(array_key_exists($amenity, $map_values)){

// If we do, use that value.
$mapped_amenities[] = $map_values[$amenity];

} else {
// If we don't have a value mapping you
// can perform some other action.
}

}

// Return the mapped values as a comma-separated string.
return implode(",",$mapped_amenities);

}

Here's how we call the function with our amenities element:

[soflyy_map_amenities({amenities[1]})]

Enable Custom Fields Section in Post Admin

Here we use the acf/settings/remove_wp_meta_box filter to re-enable the Custom Fields section when editing posts. The code must be outside of the Function Editor - your theme's functions.php file is a good place.

add_filter('acf/settings/remove_wp_meta_box', '__return_false');

Here are the details from the ACF developer: https://www.advancedcustomfields.com/blog/acf-pro-5-6-0-ui-ux/

cURL Error 60

Here we use the http_api_curl to temporarily disable cURL's peer SSL verification. This allows us to download from HTTPS links that don't have a valid certificate chain. However, it's a potential security issue and only used as an emergency measure while the source site is fixed. The code must be placed outside the Function Editor - such as in your theme's functions.php file.

function curl_error_60_workaround( $handle, $r, $url ) {

// Disable peer verification to temporarily resolve error 60.
curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);

}

add_action( 'http_api_curl', 'curl_error_60_workaround', 10, 3 );

Here is a useful site for checking the validity of SSL certificates: https://www.sslshopper.com/ssl-checker.html

Send Export File to FTP Destination

Here we use the pmxe_after_export action to transfer our export file via FTP. We also make use of get_attached_file() to retrieve the export file when WP All Export's Secure Mode is off.

// Parts of this code were based on code from PHP Documentation Group, which is licensed under the Creative Commons Attribution 3.0 License.
//
// You can read the Creative Commons Attribution 3.0 License here: https://creativecommons.org/licenses/by/3.0/
//
// Also, hat tip to Daniel Stenberg for some additional code inspiration: https://curl.haxx.se/libcurl/php/examples/ftpupload.html

function wpae_after_export( $export_id ) {

// Retrieve export object.
$export = new PMXE_Export_Record();
$export->getById($export_id);

// Check if "Secure Mode" is enabled in All Export > Settings.
$is_secure_export = PMXE_Plugin::getInstance()->getOption('secure');

// Retrieve file path when not using secure mode.
if ( !$is_secure_export) {
$filepath = get_attached_file($export->attch_id);

// Retrieve file path when using secure mode.
} else {
$filepath = wp_all_export_get_absolute_path($export->options['filepath']);
}

// Path to the export file.
$localfile = $filepath;

// File name of remote file (destination file name).
$remotefile = basename($filepath);

// Remote FTP server details.
// The 'path' is relative to the FTP user's login directory.
$ftp = array(
'server' => 'enter-hostname-here',
'user' => 'enter-user-here',
'pass' => 'enter-password-here',
'path' => '/enter/path/to/folder/here'
);

// Ensure username is formatted properly
$ftp['user'] = str_replace('@', '%40', $ftp['user']);

// Ensure password is formatted properly
$ftp['pass'] = str_replace(array('#','?','/','\'), array('%23','%3F','%2F','%5C'), $ftp['pass']);

// Remote FTP URL.
$remoteurl = "ftp://{$ftp['user']}:{$ftp['pass']}@{$ftp['server']}{$ftp['path']}/{$remotefile}";

// Retrieve cURL object.
$ch = curl_init();

// Open export file.
$fp = fopen($localfile, "rb");

// Proceed if the local file was opened.
if ($fp) {

// Provide cURL the FTP URL.
curl_setopt($ch, CURLOPT_URL, $remoteurl);

// Prepare cURL for uploading files.
curl_setopt($ch, CURLOPT_UPLOAD, 1);

// Provide the export file to cURL.
curl_setopt($ch, CURLOPT_INFILE, $fp);

// Provide the file size to cURL.
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localfile));

// Start the file upload.
curl_exec($ch);

// If there is an error, write error number & message to PHP's error log.
if($errno = curl_errno($ch)) {
if (version_compare(phpversion(), '5.5.0', '>=')) {

// If PHP 5.5.0 or greater is used, use newer function for cURL error message.
$error_message = curl_strerror($errno);

} else {

// Otherwise, use legacy cURL error message function.
$error_message = curl_error($ch);
}

// Write error to PHP log.
error_log("cURL error ({$errno}): {$error_message}");

}

// Close the connection to remote server.
curl_close($ch);

} else {

// If export file could not be found, write to error log.
error_log("Could not find export file");

}
}

add_action('pmxe_after_export', 'wpae_after_export', 10, 1);

Send Email After Import is Complete

Here we use the pmxi_after_xml_import action to email the results of our import to [email protected]. We make use of the WPDB class to retrieve the import stats and wp_mail() to send the email.

function wpai_send_email($import_id)
{
// Only send emails for import ID 1.
if($import_id != "1")
return;

// Retrieve the last import run stats.
global $wpdb;
$table = $wpdb->prefix . "pmxi_imports";

if ( $soflyyrow = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM `" . $table . "` WHERE `id` = '%d'", $import_id ) ) ) {

$count = $soflyyrow->count;
$imported = $soflyyrow->imported;
$created = $soflyyrow->created;
$updated = $soflyyrow->updated;
$skipped = $soflyyrow->skipped;
$deleted = $soflyyrow->deleted;

}

// Destination email address.
$to = '[email protected]';

// Email subject.
$subject = 'Import ID: '.$import_id.' complete';

// Email message.
$body = 'Import ID: '.$import_id.' has completed at '. date("Y-m-d H:m:s"). "rn" . 'File Records:' .$count."rn".'Records Imported:'.$imported."rn".'Records Created:'.$created;
$body .= "rn" . 'Records Updated:'. $updated . "rn" . 'Records Skipped:' . $skipped . "rn" . 'Records Deleted:' . $deleted;

// Send the email as HTML.
$headers = array('Content-Type: text/html; charset=UTF-8');

// Send via WordPress email.
wp_mail( $to, $subject, $body, $headers );
}

add_action('pmxi_after_xml_import', 'wpai_send_email', 10, 1);

Append ACF Repeater Data

Here we use the pmxi_saved_post action to append a row to the repeater_text field of our basic_repeater. This workaround is required since our source file has repeater data spread across multiple rows. We use the add_row() function to save our new repeater row.

We use an Existing Items import since our ACF posts already exist. Then we store our value to append in the my_repeater_data custom field:

We tell WP All Import to update the my_repeater_data field:

add_action( 'pmxi_saved_post', 'soflyy_add_data', 10, 3 );

function soflyy_add_data( $id, $xml, $update ) {

// Parent field name.
$selector = 'basic_repeater';

// The field to be appended.
$subfield1 = 'repeater_text';

// Only continue if my_repeater_data contains a value.
if ( $value = get_post_meta( $id, 'my_repeater_data', true ) ) {

// Format data for repeater.
$row = array( $subfield1 => $value );

// Add new repeater row.
add_row( $selector, $row, $id );

}
delete_post_meta( $id, 'my_repeater_data' );
}

Custom Items Loop for WooCommerce Orders Exports

Here we use custom code to modify the format of exported Order items. In this case, we need one product per item element instead of all products in items directly. Here's our Custom XML template:

{Product ID}
{SKU}
{Quantity}
{Item Cost}

The output would look like this:

197
195
KEYB3
KEYB2
1
5
30.00
30.00

To achieve the desired format, we need to reconfigure our export template to instead call our custom function:

[my_output_items({SKU},{Product ID},{Quantity},{Item Cost})]

And disable the automatic use of CDATA tags:

Then our exported format becomes:

197
KEYB3
1
30.00

195
KEYB2
5
30.00

We save this code to WP All Export's Function Editor:

function my_output_items( $skus = '', $ids = '', $qty = '', $cost = '' ) {

// Declare our variable to store the new XML.
$xml = '';

// Ensure $skus isn't empty and that it's an array.
if ( !empty( $skus ) && is_array( $skus ) ) {

// Process each SKU in the array.
foreach ( $skus as $key => $value ) {

// Add the opening item tag.
$xml .= "**LT**item**GT**";

// Add the Product ID tags and value.
$xml .= "**LT**ProductID**GT**" . ( empty( $ids[ $key ] ) ? '' : $ids[ $key ] ) . "**LT**/ProductID**GT**";

// Add the SKU tags and value.
$xml .= "**LT**SKU**GT**" . ( empty( $value ) ? '' : $value ) . "**LT**/SKU**GT**";

// Add the Quantity tags and value.
$xml .= "**LT**Quantity**GT**" . ( empty( $qty[ $key ] ) ? '' : $qty[ $key ] ) . "**LT**/Quantity**GT**";

// Add the ItemCost tags and value.
$xml .= "**LT**ItemCost**GT**" . ( empty( $cost[ $key ] ) ? '' : $cost[ $key ] ) . "**LT**/ItemCost**GT**";

// Add the closing item tag.
$xml .= "**LT**/item**GT**";
}

// If $skus isn't an array handle it here.
} else {

// Add the opening item tag.
$xml .= "**LT**item**GT**";

// Add the ProductID tags and value.
$xml .= "**LT**ProductID**GT**" . ( empty( $ids ) ? '' : $ids ) . "**LT**/ProductID**GT**";

// Add the SKU tags and value.
$xml .= "**LT**SKU**GT**" . ( empty( $skus ) ? '' : $skus ) . "**LT**/SKU**GT**";

// Add the Quantity tags and value.
$xml .= "**LT**Quantity**GT**" . ( empty( $qty ) ? '' : $qty ) . "**LT**/Quantity**GT**";

// Add the ItemCost tags and value.
$xml .= "**LT**ItemCost**GT**" . ( empty( $cost ) ? '' : $cost ) . "**LT**/ItemCost**GT**";

// Add the closing item tag.
$xml .= "**LT**/item**GT**";
}

return $xml;

}

You can learn more about PHP functions in custom XML exports via the "Help" button: 

Count Number of Created Posts

Here we use the pmxi_after_xml_import action to count the number of posts created during the import. Using that number we can selectively perform other tasks.

function count_created_posts( $import_id ){

// Only count created posts for import ID 1.
if( $import_id == 1 ){

// Query the database directly.
global $wpdb;
$import = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM `" . $wpdb->prefix . "pmxi_imports` WHERE ID = %d;", $import_id ) );

// Define the number of created posts.
$created = $import->created;

if ( $created > 0 ) {

// Do something if posts were created during import.

}
}
}

add_action('pmxi_after_xml_import', 'count_created_posts', 10, 1);

Cancel Import If File Empty

Here we use the pmxi_before_xml_import action to cancel the import if less than 100 records are in the file. This is especially important if using the 'Delete posts that are no longer present in your file' option (Step 4 or Manage Imports > Import Settings). When using that option, this keeps the previously imported records intact if the feed fails to return data.

function cancel_empty_import( $importID ) {

// Retrieve import object.
$import = new PMXI_Import_Record();
$import->getById($importID);

// Ensure object is valid and check if less than 100
// records are in the file.
if ( !$import->isEmpty() && $import->count getBy( 'import_id', $importID );

// Ensure object is valid.
if ( !$history_file->isEmpty() ) {

// Retrieve import file path.
$file_to_import = wp_all_import_get_absolute_path( $history_file->path );

// If file is empty or has less than 100 records,
// cancel import.
if ( file_exists( $file_to_import ) and filesize( $file_to_import ) === 0 or $import->count set( array(
'queue_chunk_number' => 0,
'processing' => 0,
'imported' => 0,
'created' => 0,
'updated' => 0,
'skipped' => 0,
'deleted' => 0,
'triggered' => 0,
'executing' => 0
))->update();

// Display the reason the import was cancelled.
echo 'Import skipped because of empty file / < 100 records';

// Stop the import.
die();
}
}
}
}

add_action( 'pmxi_before_xml_import', 'cancel_empty_import', 10, 1 );

Delete Source File After Import

Here we use the pmxi_after_xml_import action to delete the source file once imported. This can prevent the same import file from being imported twice. It's also helpful if you're pushing the import file to your server via FTP and need to know when it has been processed.

function delete_import_file( $import_id ) {

// Retrieve import object.
$import = new PMXI_Import_Record();
$import->getById( $import_id );

// Confirm import object is valid.
if ( ! $import->isEmpty() ) {

// Retrieve file information.
$history_file = new PMXI_File_Record();
$history_file->getBy( 'import_id', $import_id );

// Confirm file isn't empty.
if ( !$history_file->isEmpty() ) {

// Retrieve file path.
$import_file = wp_all_import_get_absolute_path( $history_file->path );

// Mark file for deletion.
@unlink( $import_file );

}
}
}

add_action( 'pmxi_after_xml_import', 'delete_import_file', 10, 1 );

Manually Set Import Feed Type

Here we use the wp_all_import_feed_type filter to specify we are importing an XML feed. This may be necessary if your feed URL doesn't end with the type - csv, xml, json, etc. This code must be called outside of WP All Import's Function Editor in a place such as your theme's functions.php file.

function set_feed_type( $type, $url ){

// Specify Feed URL.
if ($url == 'https://www.example.com/feedurl'){

// Set feed type.
$type = 'xml';

}

// If URL doesn't match our feed return the default type.
return $type;

}

add_filter( 'wp_all_import_feed_type', 'set_feed_type', 10, 2 );

Combine HTML Elements In XML Without CDATA Tags

Here we use custom code to copy the HTML in our XML file to its own element. This is required to import that HTML since it wasn't encoded or wrapped in CDATA tags. Otherwise, the individual HTML tags will be processed as XML tags by WP All Import. It's best to have a properly formed XML file, but we can fix it with code.

Our HTML is stored in the content element in our file. Our code loads that element and ensures it's not empty. Then keywords in our HTML are replace with their HTML tag equivalents. Finally, we write the HTML to its own element, content_html, so we can use it in our import.

function parse_content($node){

// Our element containing the HTML to process.
$result = $node->xpath('content');

// Ensure a value was returned.
if (!empty($result[0])) {

// Replace keywords with HTML equivalents.
$find_xml = array('section_title','section_content','section', 'texteparagraphe','titreparagraphe');
$replace_html = array('h1','p','div','p','h2');
$html = str_replace($find_xml, $replace_html, $result[0]->asXML());

// Save the HTML to its own 'content_html' element.
$node->addChild('content_html', $html);
}

return $node;

}

add_filter('wpallimport_xml_row', 'parse_content', 10, 1);

Modify Record Before It's Imported

Here we use the wpallimport_xml_row filter to check the Blue element for a value. If it has one, we create an element named Color and set it to Blue. We do the same for the Red element. This works for all file types, not just XML.

Here's our starting CSV file:

NameSizeBlueRedLarge Blue HatLargeYesSmall Red HatSmallYesMedium Red and Blue HatMediumYesYes

Here's the CSV equivalent of how WP All Import sees it after our code runs:

NameSizeBlueRedColorColorLarge Blue HatLargeYesBlueSmall Red HatSmallYesRedMedium Red and Blue HatMediumYesYesBlueRed

Doing this allows the {color} XPath to return all of the Color values separated by commas. Here's what's returned for each row above:

Blue,,RedBlue,Red

function add_property_type( $node ) {
// Element to be located
$blue = $node->xpath( 'blue[1]' );

// Check if Blue element has value
if ( ! empty( $blue ) ) {
if ( ! empty( $blue[0]->__toString() ) ) {
// add Color node with value 'Blue'
$node->addChild( 'color', 'Blue' );
}
}

// Element to be located
$red = $node->xpath( 'red[1]' );

// Check if Red element has value
if ( ! empty( $red ) ) {
if ( ! empty( $red[0]->__toString() ) ) {
// add Color node with value 'Red'
$node->addChild( 'color', 'Red' );
}
}
return $node;
}

add_filter( 'wpallimport_xml_row', 'add_property_type', 10, 1 );

Append Data To A Custom Field Instead Of Overwriting

Here we are using the pmxi_saved_post action to append an additional value to the your_meta_key field. This allows importing values from multiple rows in our file to a single post's custom field. We must import the new value to the _temp custom field first:

The field you append must not be updated by WP All Import or the original value will be lost (Step 4 or Manage Imports > Import Settings):

function custom_field_append($id)
{
// Get the current value of your meta key.
$value = get_post_meta($id, 'your_meta_key', true);

// Get the temp value we imported.
$temp = get_post_meta($id, '_temp', true);

// Append the temp value to the original value and
// save it to your meta key
update_post_meta($id, 'your_meta_key', $value . $temp);

// Delete the temp field.
delete_post_meta($id, '_temp');

}

add_action('pmxi_saved_post', 'custom_field_append', 10, 1);

Update Second Custom Field If First Field Has Specified Value

Here we use the pmxi_saved_post action, get_post_meta(), and update_post_meta() to only update _my_custom_field if _my_update_check is set to 'yes'. The new value is stored in _my_new_value during import and copied to _my_custom_field as needed.

function conditional_update($id)
{

// Retrieve field to check.
$check = get_post_meta($id, '_my_update_check', true);

// Check field's value and update _my_custom_field if 'yes'.
if ($check === 'yes') {

// Retrieve the new value.
$new_value = get_post_meta($id, '_my_new_value', true);

// Save the new value to the target field.
update_post_meta($id, '_my_custom_field', $new_value);
}
}

add_action('pmxi_saved_post', 'conditional_update', 10, 1);

Use Parent Prices For Variations

Here we use the pmxi_saved_post action to assign the _parent_price value to each variation. This is necessary because our file only lists parent prices. The get_post_meta() function is used to retrieve _parent_price. Then wc_get_product() is used to get the product's object

If you're trying to use a price update import and only have a file with prices for the parent products, you'll need some custom code to apply those prices to the variations.

Be sure to set the price to a custom field named '_parent_price'. Then the code below should be helpful:

function my_set_price($post_id) {

// Get the parent price.
$parent_price = get_post_meta($post_id, "_parent_price", true);

// Get the parent product object.
$parent = wc_get_product($post_id);

// Check if it's a variable product.
if( $parent && 'variable' == $parent->get_type() ){

// Get product's variations.
$variations = $parent->get_children();
// Loop through the variations and set the price
foreach ($variations as $variation) {
$single_variation = wc_get_product($variation);
$variation_id = $single_variation->get_variation_id();
update_post_meta($variation_id, "_price", $parent_price);
update_post_meta($variation_id, "_regular_price", $parent_price);
}
delete_post_meta($post_id, "_parent_price");

}
}
add_action( 'pmxi_saved_post', 'my_set_price', 10, 1 );

Access Data Outside of Element Chosen on Step 2

Here we use the pmxi_before_xml_import action to add Status as a child to each Procurement element, allowing it to be selected on Step 3. Note that this workaround only works for XML imports.

Here is the original XML structure:

32434


On

16683

Description

16684

Description 2

On Step 3 we can't select Status since it's not a child of the element chosen on Step 2, Procurement:

After the code runs, the file looks like this:

32434
On

16683

Description


On

16684

Description 2


On

Making Status available on Step 3:

Note: Status won't show up in the list until the import first runs. But, it can be typed in manually and used: {Status[1]}

function wpai_pmxi_before_xml_import( $importID ) {

// Retrieve import object.
$import = new PMXI_Import_Record();
$import->getById( $importID );

// Ensure import object is valid.
if ( ! $import->isEmpty() ) {

// Retrieve history file object.
$history_file = new PMXI_File_Record();
$history_file->getBy( 'import_id', $importID );

// Ensure history file object is valid.
if ( ! $history_file->isEmpty() ) {

// Retrieve import file path.
$file_to_import = wp_all_import_get_absolute_path( $history_file->path );

// Load import file as SimpleXml.
$file = simplexml_load_file( $file_to_import );

// Check if Status is a child of Procurement.
$query = $file->xpath( "//Apartment/Procurements[1]/Procurement[1]/Status[1]" );
if ( ! empty( $query ) ) {

// If it is, do nothing.
return;

}

// Get Status value.
$iquery = $file->xpath( "//Apartment/Status[1]" );

// Ensure value isn't empty.
if ( ! empty( $iquery ) ) {

// Value of status as string.
$status = $iquery[0]->__toString();

// Target path.
$new_query = $file->xpath( "./Procurements/Procurement" );

// Ensure path is valid.
if ( ! empty( $new_query ) ) {

// Process each Procurement element.
foreach ( $new_query as $record ) {

// Ensure this element doesn't have Status.
if ( ! isset( $record->Status ) ) {

// Add {Status[1]} as child node.
$record->addChild( 'Status', $status );

}
}

// Save updated file.
$updated_file = $file->asXML( $file_to_import );

}
}
}
}
}

add_action( 'pmxi_before_xml_import', 'wpai_pmxi_before_xml_import', 10, 1 );

Map Values to Element Name Based on Value

Here we use the wpallimport_xml_row filter to map our boolean fields to a new element with string values. This is needed since our file contains one element for each feature with a value of either 0 or 1. All fields with a value of 1 must have their name copied to a new element for import.

Here's an example of our file:

NamepoolpatiobasementdeckairconditioningVilla11000House01111Apartment10001

We can handle this mapping with some custom code. The end result represented as CSV is below:

NamepoolpatiobasementdeckairconditioningmycustomfeaturesVilla11000Pool, PatioHouse01101Patio, Basement, Air ConditioningApartment10001Pool, Air Conditioning

That allows using the {mycustomfeatures[1]} element in our import configuration as needed (Step 3 or Manage Imports > Edit Import).

function add_feature_node( $node ) {

// Our mappings from element name to string.
$fields = array(
"pool" => "Pool",
"patio" => "Patio",
"basement" => "Basement",
"deck" => "Deck",
"airconditioning" => "Air Conditioning"
);

// Process each element.
foreach ( $fields as $key => $field ) {

// Retrieve the field's value.
$result = $node->xpath( $key . '[1]' );

// Check if element doesn't exist or contains an empty value
if ( !isset( $result[0] ) || empty( $result[0]->__toString() ) ) {

// If so, don't list that feature.
unset( $fields[ $key ] );

}

}

// Add the comma separated features to our new element.
$node->addChild( 'mycustomfeatures', implode( ",", $fields ) );

return $node;
}

add_filter( 'wpallimport_xml_row', 'add_feature_node', 10, 1 );

Match By SKU When The SKUs On Your Site Contain Extra Characters

Here we use the WPDB class' get_var() method to retrieve the products matching our SKUs. Using REPLACE we ignore an unwanted slash that's used in each SKU on the site, but not in our file: my/sku vs mysku

function get_product_by_sku( $sku ) {

// Access WPDB object.
global $wpdb;

// Match by our files SKU and ignore slashes in the database.
$product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND REPLACE(meta_value, '/', '')='%s' LIMIT 1", $sku ) );

// If a match was found return its ID.
if ( $product_id ) return $product_id;

return null;
}

We call our function on Step 4 of an Existing Items import. Our files SKU element is passed as the only parameter:

Import ACF Post2Post Plugin Bidirectional Fields

Here we use the pmxi_acf_custom_field filter to allow the ACF Post2Post plugin to link Bidirectional fields.

function import_set_bidiretional_fields( $value, $post_id, $name ) {

// Get ACF field object by name.
$field = get_field_object($name, $post_id);

// Take no action if field is empty.
if( empty($field) ) {
return $value;
}

// Apply update_value filter to trigger acf-post2post plugin.
if ( $field['type'] == 'relationship' ) {
$value = apply_filters( "acf/update_value/type={$field['type']}", $value, $post_id, $field );
}

// Return the original field value.
return $value;
}

add_filter( 'pmxi_acf_custom_field', 'import_set_bidiretional_fields', 10, 3 );

Import Images to WP User Avatar Plugin

Here we use the pmxi_saved_post action to save images to the WP User Avatar plugin. Using get_user_meta() we retrieve the avatar URL from the custom field. The media_sideload_image() function saves the image to WordPress. We make use of add_action() and remove_action() to call my_get_id() which links the avatar to the User with update_user_meta(). This works when importing Users or Customers.

The link to each image must be saved in wp_user_avatar when importing:

function my_import_avatar_image ($id) {

// Get the URL for the avatar image.
$image_url = get_user_meta($id, "wp_user_avatar", true);

// Set the my_get_id function to run when attachments added.
add_action( 'add_attachment', 'my_get_id' );

// Sideload the avatar image.
$new_url = media_sideload_image($image_url, $id, null, "src");

// Remove the my_get_id function from add_attachment action.
remove_action( 'add_attachment', 'my_get_id' );
}

function my_get_id( $att_id ) {

// Get the post object for the attachment.
$img = get_post( $att_id );

// Get the attachment's parent.
$user = $img->post_parent;

// Set wp_user_avatar to the image's ID.
update_user_meta( $user, "wp_user_avatar", $att_id );
}

add_action('pmxi_saved_post', 'my_import_avatar_image', 10, 3);

This code has been derived from the following example: http://wordpress.stackexchange.com/a/46365/108630

Import Data to Custom Database Table

Here we use the pmxi_saved_post action to save data to a custom database table while importing to a defined post type. The value for the custom database table is saved to _your_temp_field. Then retrieved using get_post_meta() and written to the database using the wpdb object. To cleanup, we remove the temporary custom field using delete_post_meta().

function save_data_to_custom_database_table($id)
{
// Make wpdb object available.
global $wpdb;

// Retrieve value to save.
$value = get_post_meta($id, '_your_temp_field', true);

// Define target database table.
$table_name = $wpdb->prefix . "your_table_name";

// Insert value into database table.
$wpdb->insert($table_name, array('post_id' => $id, 'column_name' => $value), array('%s','%s'));

// Delete temporary custom field.
delete_post_meta($id, '_your_temp_field');
}

add_action('pmxi_saved_post', 'save_data_to_custom_database_table', 10, 1);

Include Featured Image in Product Gallery

Here we use the wp_all_import_variable_product_imported action to prepend the featured image to the product's gallery. We use get_post_meta() to retrieve current gallery images and get_post_thumbnail_id()for the featured image. It's added to the list of gallery images, duplicates are removed, then update_post_meta() saves the updates.

function copy_featured_img_to_gallery($post_id){

// Retrieve the Product Gallery image IDs.
$gallery = explode(",",get_post_meta($post_id, "_product_image_gallery", true));

// Add the Featured Image to the Gallery IDs.
array_unshift($gallery, get_post_thumbnail_id( $post_id ));

// Ensure no image IDs are duplicated in the Gallery.
$gallery = array_unique($gallery);

// Save the updated list of Gallery image IDs.
update_post_meta($post_id, "_product_image_gallery", implode(",",$gallery));

}

add_action('wp_all_import_variable_product_imported', 'copy_featured_img_to_gallery', 10, 1);

List One 'Any Attribute' Variation for Variable Products

Here we use the wp_all_import_variable_product_imported action to delete all but one variation and set its attributes to 'any'. This may be useful if you have a large number of Attribute options, but each variation's details ( price, stock, etc ) are all the same and don't need to be tracked separately.

The updated products look something like below:

function set_attributes_to_any( $post_parent ){
global $wpdb;
$table = $wpdb->posts;

// Retrieve all variations for this product.
$variations = $wpdb->get_results("SELECT * FROM $table WHERE post_parent = " . $post_parent );
if ( ! empty($variations)){

// Keep one variation.
$empty_variation = array_shift($variations);
if ( ! empty($variations)){

// Delete all other variations for this product.
foreach($variations as $variation){
wp_delete_post($variation->ID);
}
}
$table = _get_meta_table('post');

// Find all Attributes set for our remaining variation.
$post_meta = $wpdb->get_results("SELECT meta_key, meta_value FROM $table WHERE post_id = " . $empty_variation->ID . " AND meta_key LIKE 'attribute%';" );

// Make Attributes show as 'any'.
if ( ! empty($post_meta)){
foreach ($post_meta as $meta) {
update_post_meta($empty_variation->ID, $meta->meta_key, '');
}
}
}
}

add_action( 'wp_all_import_variable_product_imported', 'set_attributes_to_any', 10, 1 );

Only Import Existing Taxonomy Terms

Here we use the pmxi_single_category filter to prevent WP All Import from creating new taxonomy terms. Only existing terms will be assigned to records during import. This works for all taxonomies - categories, tags, etc.

function dont_create_terms( $term_into, $tx_name ) {

// Check if term exists, checking both top-level and child
// taxonomy terms.
$term = empty($term_into['parent']) ? term_exists( $term_into['name'], $tx_name, 0 ) : term_exists( $term_into['name'], $tx_name, $term_into['parent'] );

// Don't allow WP All Import to create the term if it doesn't
// already exist.
if ( empty($term) and !is_wp_error($term) ) {
return false;
}

// If the term already exists assign it.
return $term_into;

}

add_filter( 'pmxi_single_category', 'dont_create_terms', 10, 2 );

Do Not Remove Certain Categories During Import

Here we use the wp_all_import_set_post_terms filter and get_the_terms() to ensure the 'Featured' category remains for any product that had it manually assigned. This allows WP All Import to add and remove other categories without losing our manual assignments.

function dont_remove_featured_category( $term_taxonomy_ids, $tx_name, $pid, $import_id ) {

// Only check Product Categories.
if ( $tx_name == 'product_cat' ){

// Retrieve all currently assigned categories.
$txes_list = get_the_terms($pid, $tx_name);

// Do nothing if no categories are set.
if ( ! empty($txes_list) ){
foreach ($txes_list as $cat){

// If category name is 'Featured' add it to import.
if ($cat->name == 'Featured'){
$term_taxonomy_ids[] = $cat->term_taxonomy_id;
break;
}
}
}
}

// Return the updated list of taxonomies to import.
return $term_taxonomy_ids;

}

add_filter( 'wp_all_import_set_post_terms', 'dont_remove_featured_category', 10, 4 );

Use Custom Delimiter When Retrieving Multiple Values

Here we use the wp_all_import_multi_glue filter to separate multiple returned values with pipes instead of commas.

Here's an example CSV file:

NameSizeColorColorRed and Blue ShirtMediumRedBlue

If we retrieve both Color elements using a single XPath statement such as {Color}, by default they will be returned comma-delimited: Red, Blue

The example code will change that comma to a pipe: Red|Blue

add_filter( 'wp_all_import_multi_glue', function( $delimiter ){
return '|';
}, 10, 1 );

Only Update Custom Field If New Value Is Not Empty

Here we use the pmxi_custom_field filter to only overwrite _my_custom_field if the new value isn't empty. This is helpful if the field should always have a value and your file doesn't always provide it.

function keep_existing_if_empty($value, $post_id, $key, $original_value, $existing_meta, $import_id)
{

// Only check _my_custom_field.
if ($key == '_my_custom_field') {

// Check if it has a value.
if (empty($value)) {

// If empty, use the existing value.
$value = isset($existing_meta[$key][0]) ? $existing_meta[$key][0] : $value;

}
}

return $value;

}

add_filter('pmxi_custom_field', 'keep_existing_if_empty', 10, 6);

Only Update Custom Field If It's Currently Empty

Here we use the pmxi_custom_field filter to only update _my_custom_field if it's currently empty. This allows for making manual changes to fields that won't be overwritten during import.

function update_existing_if_empty($value, $post_id, $key, $existing_meta, $import_id)
{

// Only process import ID 5.
if ($import_id == 5) {

// Only check _my_custom_field.
if ($key == '_my_custom_field') {

// Check if field exists and if it's empty.
if (!isset($existing_meta[$key][0]) || empty($existing_meta[$key][0])) {

// Use existing value only if currently empty.
$value = $existing_meta[$key][0];

}
}
}

return $value;

}

add_filter('pmxi_custom_field', 'update_existing_if_empty', 10, 5);

Only Update ACF Fields If Imported Values Are Not Empty

Here we use the pmxi_acf_custom_field filter and get_post_meta() to prevent writing empty values to existing ACF fields.

function wp_all_import_pmxi_acf_custom_field( $value, $pid, $name ) {

// Retrieve existing ACF field value.
$existing = get_post_meta( $pid, $name, true );

if ( empty( $value ) ) {

// If the new value is empty, use existing value.
$value = $existing;

}

return $value;

}

add_filter( 'pmxi_acf_custom_field', 'wp_all_import_pmxi_acf_custom_field', 10, 3 );

Filter Posts by Date

Here we use the wp_all_import_is_post_to_create and wp_all_import_is_post_to_update filters to create or update records based on a date in our file. Such code is necessary since the visual filters won't work with date fields.

We retrieve the date from column_4 in our file. Then we specify 2018-10-10 as our target date before converting it to a Unix timestamp. We include those newer than our target date in the import.

function filter_by_date( $continue_import, $data, $import_id ) {

// Only apply to import ID 3
if ( $import_id == 3 ) {

// Change 'column_4' to your file's date field.
$date_in_file = strtotime( $data['column_4'] );

// Change '2018-10-10' to your real target date.
$date = "2018-10-10";

// Convert specified date to Unix timestamp.
$target_date = strtotime($date);

// Compare file date with target date.
if ( $date_in_file >= $target_date ) {

// Create or update record if file date >= target date.
return true;

} else {

// Do not create or update the record otherwise.
return false;

}
}

// Take no action for other import IDs.
return $continue_import;

}

// Apply the code to posts set to be created.
add_filter('wp_all_import_is_post_to_create', 'filter_by_date', 10, 3);

// Apply the code to posts set to be updated.
add_filter('wp_all_import_is_post_to_update', 'filter_by_date', 10, 3);

Only Create Post If Custom Field Value Is Unique

Here we use the wp_all_import_is_post_to_create filter to only create records if they have a unique value for my_custom_field. This prevents duplicates when importing overlapping records using multiple 'New Items' imports. We use WP_Query to check for existing posts with the same value.

function create_only_if_unique_custom_field( $continue_import, $data, $import_id )
{
// Only run for import ID 1.
if ($import_id == 1) {

// The custom field to check.
$key_to_look_up = "my_custom_field";

// The value to check where 'num' is the element name.
$value_to_look_up = $data['num'];

// Prepare the WP_Query arguments
$args = array (

// Set the post type being imported.
'post_type' => array( 'post' ),

// Check our custom field for our value.
'meta_query' => array(array(
'key' => $key_to_look_up,
'value' => $value_to_look_up,
)),
);

// Run the query and do not create post if custom
// field value is duplicated.
$query = new WP_Query( $args );
return !($query->have_posts());

} else {

// Take no action if a different import ID is running.
return $continue_import;

}
}

add_filter('wp_all_import_is_post_to_create', 'create_only_if_unique_custom_field', 10, 3);

Do Not Create Products With Duplicate SKU

Here we use the wp_all_import_is_post_to_create filter to avoid creating products with duplicate SKUs. This helps when the same product may appear in multiple 'New Items' imports. For fields other than SKU use this snippet.

function dont_duplicate_skus( $continue_import, $data, $import_id ) {

// Get the SKU from the import file.
// Change 'sku' to the column name that contains your SKU.
$sku = $data['sku'];

// Enable access to global wpdb object.
global $wpdb;

// Check if the SKU already exists.
$product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku ) );

if ( $product_id ) {

// If product ID exists then skip importing.
return false;

} else {

// Else, import the product.
return true;

}
}

add_filter('wp_all_import_is_post_to_create', 'dont_duplicate_skus', 10, 3);

Only Update Posts with a Specific Status

Here we use the wp_all_import_is_post_to_update filter to only update records that are set to 'draft'. We use get_post_status() to retrieve the current status of each record to determine if it should be updated.

function my_check_post_status ($continue_import, $post_id, $data, $import_id ) {

// Only process import ID 1.
if ($import_id == 1) {

// Retrieve post status.
$my_post_status = get_post_status($post_id);

// Check if status is set to draft.
if ($my_post_status == "draft") {

// Tell WP All Import to update post if it's draft.
return true;
}

// Do not update post if it's not set to draft.
return false;

}
else {

// Do nothing if it's not our specified import ID.
return $continue_import;

}
}

add_filter( 'wp_all_import_is_post_to_update', 'my_check_post_status', 10, 4 );

Only Update Products If The Stock Status Changed

Here we use the wp_all_import_is_post_to_update filter and get_post_meta to compare each product's _stock_status with the status in our file. If they match the product won't be updated.

function only_update_if_stock_status_changed ( $continue_import, $post_id, $data, $import_id ) {

// Only run for import ID 1.
if ($import_id == 1) {

// Get stock status from the import file.
// Our file element is named 'stock_status'.
$import_stock_status = $data["stock_status"];

// Retrieve product's current stock status.
$woo_stock_status = get_post_meta($post_id, "_stock_status", true);

// Check if import_stock_status and
// woo_stock_status match.
if ($import_stock_status !== $woo_stock_status) {

// Update the product if they don't.
return true;

}else{
// Otherwise, don't update the product.
return false;
}
}

// Do nothing if it's not our target import.
return $continue_import;

}

add_filter( 'wp_all_import_is_post_to_update', 'only_update_if_stock_status_changed', 10, 4 );

Only Update Product If Price Was Not Manually Changed

Here we use the wp_all_import_is_post_to_update filter and get_post_meta() to check if _last_imported_price matches the currently set price. This prevents updating the product if the price was manually changed. The _last_imported_price custom field must be set to your price element:

function do_not_update_if_price_hand_modified( $continue_import, $post_id, $data, $import_id ) {

// Only run for import ID 1.
if ($import_id == 1) {

// Retrieve last imported price.
$imported_price = get_post_meta($post_id, "_last_imported_price", true);

// If price isn't greater than zero update the product.
if ($imported_price <= 0)
return true;

// Update product if the new prices matches that
// last imported.
if ($imported_price === get_post_meta($post_id, "_price", true))
return true;

// Do not update product otherwise.
return false;
}

else {

// Take no action if it's not our import ID.
return $continue_import;

}
}

add_filter( 'wp_all_import_is_post_to_update', 'do_not_update_if_price_hand_modified', 10, 4 );

Delete Orphaned Variations When Deleting Previously Imported Products

Here we use the wp_all_import_is_post_to_delete filter to only delete variations created by WP All Import that have been orphaned. This is needed as our import file won't always list all of the variations. Also, we need to make sure we don't leave orphaned variations after deleting a parent product. The 'Delete products that are no longer present in your file' option must be checked or the code does nothing (Manage Imports > Import Settings).

function is_post_to_delete_orphans($to_delete, $pid, $import)
{

// Only process products.
if ($import->options['custom_type'] == 'product') {

// Retried product post object.
$post_to_delete = get_post($pid);

// Check if parent or variation.
switch ($post_to_delete->post_type) {

// Mark to delete if parent.
case 'product':
$to_delete = true;
break;

// Process variations.
case 'product_variation':
$parent_product = get_post($post_to_delete->post_parent);

// Only delete variation if orphaned.
$to_delete = empty($parent_product) ? true : false;
break;
}
}

return $to_delete;

}

add_filter('wp_all_import_is_post_to_delete', 'is_post_to_delete_orphans', 11, 3);

Prevent Some Posts From Being Deleted By WP All Import

Here we use the wp_all_import_is_post_to_delete filter and get_post_meta() to avoid deleting records that have a do_not_delete custom field. This allows manually specifying that some products should not be deleted even if they're missing from your file.

It only works when using the 'Delete products that are no longer present in your file' option (Step 4 or Manage Imports > Import Settings).

function is_post_to_delete_on_meta($is_post_to_delete, $pid, $import)
{

// Check if a custom field named 'do_not_delete' exists and
// do not delete record if it does.
return $var = ( get_post_meta($pid, 'do_not_delete', true) ) ? false : true;

}

add_filter('wp_all_import_is_post_to_delete', 'is_post_to_delete_on_meta', 10, 3);

Save Images To Your Theme's or Plugin's Custom Gallery

Here we use the pmxi_gallery_image action to add images to a few common gallery field configurations. We use get_post_meta() to retrieve the existing gallery fields and update_post_meta() to save the new updated galleries. When working with named key arrays we use wp_get_attachment_image_src() to retrieve the image URLs.

We import our images using the Images section (Step 3 or Manage Imports > Edit Import).

Named Key Arrays ([image_id_1 => "image_url_1"]):

function gallery_id_url($post_id, $att_id, $filepath, $is_keep_existing_images = '')
{

// The custom field used by the gallery.
$key = '_gallery';

// The image size to list in the gallery.
$size = 'full';

// Retrieve the current gallery value.
$gallery = get_post_meta($post_id, $key, TRUE);

// If it's empty declare a new array.
if (empty($gallery)) {
$gallery = array();
}

// Check if image is already in the gallery.
if (!isset($gallery[$att_id])) {

// If not, retrieve the image's URL.
$src = wp_get_attachment_image_src($att_id, $size);

// Add the image ID and URL to our gallery.
$gallery[$att_id] = $src[0];

// Save the gallery.
update_post_meta($post_id, $key, $gallery);

}
}

add_action('pmxi_gallery_image', 'gallery_id_url', 10, 4);

Numeric Array Keys ([0 => image_id_1, 1 => image_id_2]):

function gallery_n_id($post_id, $att_id, $filepath, $is_keep_existing_images = '')
{

// The custom field used for the gallery.
$key = '_gallery';

// Retrieve existing gallery values.
$gallery = get_post_meta($post_id, $key, TRUE);

// If gallery is empty declare a new array.
if (empty($gallery)) {
$gallery = array();
}

// Check if the image is in the gallery.
if (!in_array($att_id, $gallery)) {

// If not, add it.
$gallery[] = $att_id;

// Save the new gallery.
update_post_meta($post_id, $key, $gallery);

}
}

add_action('pmxi_gallery_image', 'gallery_n_id', 10, 4);

Individual Post Meta (custom field) per image:

function gallery_meta_id($post_id, $att_id, $filepath, $is_keep_existing_images = '')
{

// The custom field used for the gallery.
$key = '_gallery'; // Edit this: Set meta key for gallery array here

// Retrieve the current gallery values.
$result = get_post_meta($post_id, $key, FALSE);

// Check if the image is already in the gallery.
if (is_array($result)) {

// If not, add it.
if (!in_array($att_id, $result)) {
add_post_meta($post_id, $key, $att_id);
}

}
}

add_action('pmxi_gallery_image', 'gallery_meta_id', 10, 4);

Comma-separated image IDs (23,25,31):

function gallery_ids_in_string($post_id, $att_id, $filepath, $is_keep_existing_images = '')
{
// The custom field used by gallery.
$key = '_gallery';

// The separator to use between each ID.
$separator = ",";

// Retrieve the current values in the gallery field.
$gallery = get_post_meta($post_id, $key, true);

// Ensure gallery is valid.
if (is_string($gallery) || is_empty($gallery) || ($gallery == false)) {

// Split value into array.
$gallery = explode($separator, $gallery);

// Add image if it's not in the gallery.
if (!in_array($att_id, $gallery)) {

// Ensure array doesn't start with empty value.
if ($gallery[0] == '') unset($gallery[0]);

// Add image ID to array.
$gallery[] = $att_id;

// Save updated gallery field.
update_post_meta($post_id, $key, implode($separator, $gallery));

}
}
}

add_action('pmxi_gallery_image', 'gallery_ids_in_string', 10, 4);

Save Imported Images To A Custom Folder

Here we use the wp_all_import_images_uploads_dir filter to save our images to the uploads/customfolder directory. This can prevent saving a large number of images in a single folder when they're imported the same day.

function wpai_set_custom_upload_folder($uploads, $articleData, $current_xml_node, $import_id) {

// Change our upload path to uploads/customfolder.
$uploads['path'] = $uploads['basedir'] . '/customfolder';
$uploads['url'] = $uploads['baseurl'] . '/customfolder';

// Check if the target directory exists.
if (!file_exists($uploads['path'])) {

// If not, create the directory.
mkdir($uploads['path'], 0755, true);

}

return $uploads;

}

add_filter('wp_all_import_images_uploads_dir', 'wpai_set_custom_upload_folder', 10, 4);

Apply wp_handle_upload Filter to Uploaded Files

Here we use wp_all_import_handle_upload to apply the wp_handle_upload filter to all imported attachments and images. It's normally fired when uploading files to WordPress. However, it's disabled during imports to increase speed. Firing this filter allows plugins such as Imsanity to work for imported files.

add_filter('wp_all_import_handle_upload', function( $file ){

return apply_filters( 'wp_handle_upload', $file, 'upload');

}, 10, 1);

Set Exported Order Status

Here we use the pmxe_exported_post action to mark exported Orders as completed. We use the WC_Order method update_status() which can set any desired order status.

function set_order_status_on_export($post_id, $exportObject)
{

// Retrieve export ID.
$export_id = ( isset( $_GET['id'] ) ? $_GET['id'] : ( isset( $_GET['export_id'] ) ? $_GET['export_id'] : 'new' ) );

// Only run for export 1.
if ($export_id == "1") {

// Retrieve Order object.
        $order = new WC_Order($post_id);

// Set Order status to completed.
        $order->update_status('completed', 'export_completed');

     }
}

add_action('pmxe_exported_post', 'set_order_status_on_export', 10, 2);

Filter Variations by Parent Status When Exporting to GMC

Here we use the wp_all_export_csv_rows filter to only export variations whose parents are not set to 'draft'. This is especially useful for Google Merchant Center exports as they apply Status filters directly to variations by default.

function exclude_drafts_from_gmc_export($articles, $options, $export_id) {

// Only filter GMC exports.
if ($options["xml_template_type"] == "XmlGoogleMerchants") {

// Process every exported product.
foreach ($articles as $key => $article) {

// If IDs aren't set to be exported, do nothing.
if ( ! empty($article['id']) ) {
$post_id = $article['id'];
$parent_id = wp_get_post_parent_id($post_id);

// Check the parent's Status.
if ( get_post_status($parent_id) == "draft" ) {

// Don't export variation if parent is 'draft'.
unset($articles[$key]);

}
}
}
}

return $articles;
}
add_filter('wp_all_export_csv_rows', 'exclude_drafts_from_gmc_export', 10, 3);

Limit Records Exported to CSV by Date

Here we use the wp_all_export_csv_rows filter to only export records where _my_time is older than tomorrow. This helps when you need more control than the visual filters offer. The example works for CSV, Excel, and Google Merchant Center exports only. You must include _my_time when configuring the export.

function filter_export_by_date($articles, $options, $export_id)
{

// Process each record to be exported.
foreach ($articles as $key => $article) {

// Set target date as Unix timestamp.
$target_date = strtotime("tomorrow");

// Convert _my_time element's time to Unix timestamp.
$my_time = strtotime($article["_my_time"]);

// Check if $my_time is older that $target_date
if ($my_time < $target_date) {

// Do not export records older than $target_date.
unset($articles[$key]);

}
}

// Return updated array of records to export.
return $articles;

}

add_filter('wp_all_export_csv_rows', 'filter_export_by_date', 10, 3);

Limit Number of Records Exported When Exporting to XML

Here we use the wp_all_export_xml_rows filter to limit the number of records exported to XML. We use get_posts() to handle retrieving only the records we want to export. Our example further limits exported records to those with a specific taxonomy term set.

add_filter('wp_all_export_xml_rows', function($shouldExport, $record, $exportOptions, $exportId) {

// The Export ID to be filtered.
$exportToApplyFilterTo = 3;

// The number of records to be exported.
$lastNumberOfPostsToExport = 200;

// Do nothing if it's not our defined export ID running.
if($exportId == $exportToApplyFilterTo) {

// Retrieve the records we want to export.
$posts = get_posts(
array(
'numberposts' => $lastNumberOfPostsToExport,

// Only export published records.
'post_status' => 'publish',

// START TAXONOMY QUERY, REMOVE IF NOT FILTERING BY TAXONOMY
'tax_query' => array(
array(

// The taxonomy to check.
'taxonomy' => 'property_status',

// Search by Term ID.
'field' => 'term_id',

// The term_id of the taxonomy term.
'terms' => 575,
'include_children' => false
)
),
// END TAXONOMY QUERY

'fields' => 'ids',
'order' => 'desc',
'orderby' => 'ID',
'post_type' => $record->post_type
)
);

// Only export records found by get_posts() above.
if(in_array($record->ID, $posts)) {
return true;
} else {
return false;
}
}

// Do nothing if our defined export ID isn't running.
return $shouldExport;
}, 11, 4);

Append Custom Line Endings to Export File

Here we use the wp_all_export_after_csv_line filter to append a newline character to each row in our CSV file during export.

function custom_line_ending( $stream, $export_id ){

// Define character for line ending.
fwrite( $stream, "n" );
return $stream;

}

add_filter( 'wp_all_export_after_csv_line', 'custom_line_ending', 10, 2 );

Programmatically Add Elements To XML Exports

Here we use the wp_all_export_additional_data filter to add an element named created_at to our exported XML file.

function wpae_additional_data_field($add_data, $options)
{

// Add 'created_at' element and set its value to
// the current date and time.
$add_data['created_at'] = date("Y-m-d H:i:s");

return $add_data;

}

add_filter('wp_all_export_additional_data', 'wpae_additional_data_field', 10, 2);

Move Generated File After Export

Here we use the pmxe_after_export action to move our export file to /wp-content/uploads. The get_attached_file() function allows us to retrieve the export file path when Secure Mode is enabled. The WordPress uploads directory is located using wp_get_upload_dir().

function move_export_file ($export_id, $exportObj){

// Get WordPress's upload directory.
$upload_dir = wp_get_upload_dir();

// Check whether "Secure Mode" is enabled in All Export > Settings
$is_secure_export = PMXE_Plugin::getInstance()-> getOption('secure');

if ( !$is_secure_export ) {

// Get filepath when 'Secure Mode' is off.
$filepath = get_attached_file($exportObj->attch_id);

} else {

// Get filepath with 'Secure Mode' on.
$filepath = wp_all_export_get_absolute_path($exportObj->options['filepath']);

}

// Get the filename.
$filename = basename( $filepath );

// Move export file into /wp-content/uploads.
rename( $filepath, $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $filename );

}

add_action('pmxe_after_export', 'move_export_file', 10, 2);

Run Cron Export With Only A Trigger Cron Job and 1 Processing Cron Job

Here we use the pmxe_after_iteration action to avoid the need for multiple 'processing' cron jobs. However, if something fails on one of the iterations the export will hang. It will also hang if the process calling the 'trigger' or 'processing' URL closes the connection before the export is complete.

function wpae_continue_cron( $export_id, $exportObj ) {

// Only run for export ID 12.
if ( $export_id == '12' ) {

// Import 12's 'processing' URL.
$cron_processing_url = 'http://lame-addax-cat.w6.wpsandbox.pro/wp-cron.php?export_key=g99mni1B6Kpu&export_id=12&action=processing';

// Redirect the connection to the 'processing' URL.
header( "Location: " . $cron_processing_url . "" );
}
}

add_action( 'pmxe_after_iteration', 'wpae_continue_cron', 10, 2 );

Set Password for Imported Posts

Here we use the pmxi_saved_post action to set the password for each post as it's imported. In this case our posts all have a different password, each defined in the import file:

Every time a post is successfully imported or updated our code will retrieve the custom field's value with get_post_meta() and pass it to wp_update_post(), which will actually set the passwords.

function set_post_password( $pid ) {
wp_update_post( array(

// Specify post to update.
'ID' => $pid,

// Set password to value of custom field.
'post_password' => get_post_meta( $pid, '_password', true )
  ) );

}

add_action( 'pmxi_saved_post', 'set_post_password', 10, 1 );

Retrieve Import ID for Use in Template

Here we use some custom code to retrieve the import ID. It can be saved to a custom field to track the records updated by each import. Also, the $import_id definition can be used with our various hooks that don't provide the Import ID as a parameter.

function get_import_id(){

// Retrieve the import ID while the import is running.
$import_id = ( isset( $_GET['id'] ) ? $_GET['id'] : ( isset( $_GET['import_id'] ) ? $_GET['import_id'] : 'new' ) );

return $import_id.
}

Convert Comma-separated List into Serialized Array

Here we use custom code to convert our list element's value into a serialized array. Our theme requires this field to only contain serialized values.

Original value: red, green, blue, yellow

Value after our code: a:4:{i:0;s:3:"red";i:1;s:6:" green";i:2;s:5:" blue";i:3;s:7:" yellow";}

We call the code like this:

function list_to_serialized( $value ){

// Split the list at the commas.
$value = explode(',', $value);

// Return the serialized list.
return serialize( $value );

}

Generate Taxonomy Hierarchy from Child Terms

Here we generate a full taxonomy hierarchy for each of our terms using custom code. Our import file only contains the child terms and we don't yet have the full hierarchy on our site.

The field with our terms looks like this:2.1.1. | 2.1.4. | 2.1.5. | 3.1.1. | 3.1.2.

Our code takes our element's value and outputs the hierarchical version:2.>2.1.>2.1.1. | 2.>2.1.>2.1.4. | 2.>2.1.>2.1.5. | 3.>3.1.>3.1.1. | 3.>3.1.>3.1.2.

We must configure our import to accept that hierarchical string:

function get_hierarchy_taxonomies( $tax_str ) {

// Convert our list of terms to an array.
$tax_array = explode("|", $tax_str);

// Process each term.
foreach( $tax_array as $val ){

// Generate the top level term.
$temp = substr( trim( $val ), 0, 2 ) . '>'

// Generate the second level term.
$temp .= substr( trim( $val ), 0, 4 ) . '>'

// Add the original term to the hierarchy.
$temp .= trim( $val );

}

// Return our updated term hierarchies separated by pipes.
return implode( "|", $tax_hierarchy_array );

}

Prevent Certain Records from Being Updated

Here we use the wp_all_import_is_post_to_update filter to prevent updating records we've manually modified. This is accomplished by setting the do_not_update custom field on any record we don't want to update. We use get_post_meta() to check if do_not_update exists. If it does, we tell WP All Import not to update that record by returning false.

function is_post_to_update( $continue_import, $pid ) {

// Retrieve custom field value.
$do_not_update = get_post_meta( $pid, 'do_not_update', true );

// Update post if do_not_update field not set.
return ( !empty( $do_not_update ) ) ? false : $continue_import;

}

add_filter( 'wp_all_import_is_post_to_update', 'is_post_to_update', 10, 2 );

Bulk Attach Images to a Record's Custom Gallery

Here we use the pmxi_saved_post action to link all of our images for each property at once. This can be faster than using pmxi_gallery_image when there are many images per property (or any other post type using custom galleries).

The get_attached_media() function is used to retrieve a list of all the images attached to the property. We then confirm a value was returned and process each of those images. Once we have our list of image IDs, we save them to _image_field using update_post_meta().

add_action( 'pmxi_saved_post', 'update_gallery_ids', 10, 3 );

function update_gallery_ids( $id ) {

// Declare our variable for the image IDs.
$image_ids = array();

// Retrieve all attached images.
$media = get_attached_media( 'image', $id );

// Ensure there are images to process.
if( !empty( $media ) ) {

// Process each image.
foreach( $media as $item ) {

// Add each image ID to our array.
$image_ids[] = $item->ID;

}

// Convert our array of IDs to a comma separated list.
$image_ids_str = implode( ',',$image_ids );

// Save the IDs to the _image_field custom field.
update_post_meta( $id, '_image_field', $image_ids_str );

}
}

Split String at Delimiter and Return One Value

Here we use a custom function to divide our string at each comma. Say we have one element in our file that contains all of our Attributes like 5, Red where the first value is Size and the second is Color.

We can use our function to return each value as needed like this:

function return_list_value( $needle, $index, $string ) {

// Expect index to start at 1 instead of 0.
$index -= 1;

// Split string by $needle value.
$split = explode($needle, $string);

// Return the value at $index.
return $split[$index];

}

Retrieve URL from String

Here we use a custom function to retrieve a URL from a string. This can help if your image element has text other than the URL itself.

For example:

The function is called like this:

function find_url( $string ) {

// Find the URL.
preg_match_all('#bhttps?://[^s()]+(?:([wd]+)|([^[:punct:]s]|/))#', $string, $match);

// Return the first found URL.
return $match[0][0];

}

Round Imported Prices

Here we use a custom function to round our prices. The $price parameter is required and the others are optional. Any unwanted characters are removed from the price - anything other than 0-9, period, and comma.

It's called like this:

function round_price( $price = null, $multiplier = 1, $nearest = .01, $minus = 0 ) {

if ( !empty( $price ) ) {

// Remove any extra characters from price.
$price = preg_replace("/[^0-9,.]/", "", $price);

// Perform calculations and return rounded price.
return ( round ( ( $price * $multiplier ) / $nearest ) * $nearest ) - $minus;

}
}

Merge Multiple XML Files

This is a basic example script showing how to merge multiple XML files into 1 file before importing them with WP All Import. This script would be saved in a .php file and placed on your server, then you would point WP All Import to the script URL when setting up the import.

WPAI_Example_Merge_XML_Function parameters:

ParameterDescription$file1The full URL/path/filename to the main file$file2The full URL/path/filename to the file you want to merge into $file1$root_nodeThe root node that wraps all of the post nodes$items_nodeThe repeating post node (i.e. the one you'd choose to import on step 2 of WP All Import).$filenameThe file that the merged data will be saved into

Code:

load( $file1 );

$merge_doc = new DOMDocument();
$merge_doc->load( $file2 );

// Get root element from file1
$root = $main_doc->getElementsByTagName( $root_node )->item(0);

// Get post elements from file2
$items = $merge_doc->getElementsByTagName( $items_node );

for ( $i = 0; $i length; $i++ ) {
$node = $items->item( $i );
$import = $main_doc->importNode( $node, true );
$root->appendChild( $import );
}

$file = $main_doc->save( $filename );
}

$final_file_name = 'merged.xml';
WPAI_Example_Merge_XML_Function( 'd1.xml', 'd2.xml', 'data', 'post', $final_file_name );
WPAI_Example_Merge_XML_Function( $final_file_name, 'd3.xml', 'data', 'post', $final_file_name );
echo file_get_contents( $final_file_name );
?>

Once you've saved the script in a .php file and uploaded it to your server, you'll point WP All Import to the URL for the script in the "Download from URL" field, e.g.:

https://example.com/mergexmlscript.php

For reference, these are the XML files that this snippet example works with:

d1.xml

Post 1

d2.xml

Post 2

d3.xml

Post 3

Export parent SKU when variation SKU is blank

If your WooCommerce variations are inheriting the Parent SKU, then they will have blank SKUs in your export file. To get around this, you can use the following function on a new instance ("Add Field") of the "ID" export field:

function my_get_sku( $id ) {
$prod = wc_get_product( $id );
if ( ! $prod ) return;

if ( ! empty( $prod->get_sku() ) ) {
return $prod->get_sku();
}

if ( ! empty( $prod->get_parent_id() ) ) {
$parent = wc_get_product( $prod->get_parent_id() );
if ( ! $parent ) return;
return $parent->get_sku();
}
}

Find post by custom field value

This is a helper function that can query products, posts, custom post types, users and taxonomy terms by looking up a custom field value.

Example uses are below. Learn how to call PHP functions in imports in our inline PHP doc.

Examples

Find post with the value from {id[1]} inside the "_old_post_id" custom field:

[sf_helper_meta_query_lookup("_old_post_id",{id[1]})]

Find WooCommerce Product with value from {ean[1]} in the "_custom_ean" custom field:

[sf_helper_meta_query_lookup("_custom_ean",{ean[1]},"product")]

Find user with value from {id[1]} in the "_old_user_id" custom field (user meta field):

[sf_helper_meta_query_lookup("_old_user_id",{id[1]},"user")]

Find taxonomy term (WooCommerce Product Category in this example) with value from {cat_id[1]} in "_custom_term_id" custom field (term meta field):

[sf_helper_meta_query_lookup("_custom_term_id",{cat_id[1]},"term","product_cat")]

Code:

function sf_helper_meta_query_lookup( $meta_key = '', $meta_value = '', $object_type = 'post', $taxonomy = '', $return_field = 'ID', $compare = '=', $custom_args = array() ) {
if ( empty( $object_type ) || empty( $meta_key ) || empty( $meta_value ) ) return;
$func = 'get_posts';

switch ( $object_type ) {
case 'user':
$func = 'get_users';
break;
case 'term':
$func = 'get_terms';
if ( $return_field == 'ID' ) {
$return_field = 'term_id';
}
break;
default:
$func = 'get_posts';
break;
}

if ( ! empty( $custom_args ) ) {
$objects = $func( $custom_args );
if ( ! empty( $objects ) ) {
return $objects[0]->$return_field;
} else {
return false;
}
}

$args = array();

$meta_query = array( array(
'key' => $meta_key,
'value' => $meta_value,
'compare' => $compare
) );

if ( $func == 'get_terms' ) {
$args = array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
'meta_query' => $meta_query
);
} elseif ( $func == 'get_users' ) {
$args = array(
'meta_query' => $meta_query
);
} else {
$args = array(
'post_type' => $object_type,
'meta_query' => $meta_query
);
}

if ( $objects = $func( $args ) ) {
return $objects[0]->$return_field;
} else {
return false;
}
}

Keep old IDs when migrating products or posts

This snippet attempts to keep the old post/product ID when you migrate posts/products from one site to another. It should be saved on the import site via All Import › Settings › Function Editor.

Important note: if a post of any type exists on the new site with an ID that you're attempting to use from the old site, then this snippet won't work and a new ID will be generated for the imported post.

For users and customers see Keep old IDs when migrating users or customers.

function my_pmxi_article_data( $articleData, $import, $post_to_update, $current_xml_node ) {
// Turn the XML node into an array.
$xml_data = json_decode( json_encode( (array) $current_xml_node ), 1 );

// Change 'id' if your ID exists in an element not named {id[1]}.
$articleData['import_id'] = $xml_data['id'];

// Return article data.
return $articleData;
}
add_filter('pmxi_article_data', 'my_pmxi_article_data', 10, 4);

Keep old IDs when migrating users or customers

This snippet attempts to keep the old user/customer ID when you migrate users/customers from one site to another. It should be saved on the import site via All Import › Settings › Function Editor.

Important note: if a user/customer exists on the new site with an ID that you're attempting to use from the old site, then this snippet won't work and a new ID will be generated for the imported user/customer. Also, an ID will be auto-assigned when the user or customer is created. This snippet then tries to update that ID to the one specified.

For posts (including orders and products) see Keep old IDs when migrating products or posts.

function my_set_user_id( $post_id, $xml_node, $is_update ) {

global $wpdb;

// Retrieve the import ID.
$import_id = wp_all_import_get_import_id();

// Only run for import 1 and only run when the user is first created.
if ( $import_id == '1' && !$is_update) {

// Convert SimpleXml object to array for easier use.
$record = json_decode( json_encode( ( array ) $xml_node ), 1 );

// ID to set for user, change 'userid' to your real file element name.
$requested_id = $record['userid'];

// Check if the requested ID is already used.
$id_exists = $wpdb->get_var($wpdb->prepare('SELECT user_login FROM '.$wpdb->users.' WHERE ID = '.$requested_id));

// If the requested ID is available...
if( $id_exists === null ){

// ...assign the user ID as desired...
$wpdb->update($wpdb->users, ['ID'=> $requested_id], ['ID' => $post_id]);

// ...update the user ID for the associated meta, so the association with the user isn't lost...
$wpdb->update($wpdb->usermeta, ['user_id'=> $requested_id], ['user_id' => $post_id]);

// ...and update the ID in the pmxi_posts table, so the import can still manage this user.
$wpdb->update($wpdb->prefix . 'pmxi_posts', ['post_id'=> $requested_id], ['post_id' => $post_id, 'import_id' => $import_id]);
}

}

}
add_action( 'pmxi_saved_post', 'my_set_user_id', 10, 3 );

Keep old IDs when migrating categories and other taxonomy terms

This code tries to keep the old term ID when you migrate taxonomy terms from one site to another. It can be saved on the import site via All Import › Settings › Function Editor.

Important note: if a term in any taxonomy exists on the new site with an ID that you're attempting to use from the old site, then this code won't work and a new ID will be generated for the imported term.

For products or posts see Keep old IDs when migrating products or posts, and for users or customers see Keep old IDs when migrating users or customers.

function my_set_term_id( $post_id, $xml_node, $is_update ) {

global $wpdb;

// Retrieve the import ID.
$import_id = wp_all_import_get_import_id();

// Only run for import 1 and only run when the term is first created.
if ( $import_id == '1' && !$is_update) {

// Convert SimpleXml object to array for easier use.
$record = json_decode( json_encode( ( array ) $xml_node ), 1 );

// ID to set for term, change 'termid' to your real file element name.
$requested_id = $record['termid'];

// Check if the requested term_id is already used.
$id_exists = $wpdb->get_var($wpdb->prepare('SELECT name FROM '.$wpdb->terms.' WHERE term_id = '.$requested_id));

// If the requested term_id is available...
if( $id_exists === null ){

// ...assign the term ID as desired...
$wpdb->update($wpdb->terms, ['term_id'=> $requested_id], ['term_id' => $post_id]);

// ...update the term ID for the associated meta, so the association with the term isn't lost...
$wpdb->update($wpdb->termmeta, ['term_id'=> $requested_id], ['term_id' => $post_id]);

// ...update the term ID for the associated taxonomy, so the association isn't lost...
$wpdb->update($wpdb->term_taxonomy, ['term_id'=> $requested_id], ['term_id' => $post_id]);

// ...and update the ID in the pmxi_posts table, so the import can still manage this term.
$wpdb->update($wpdb->prefix . 'pmxi_posts', ['post_id'=> $requested_id], ['post_id' => $post_id, 'import_id' => $import_id]);
}

}

}
add_action( 'pmxi_saved_post', 'my_set_term_id', 10, 3 );

Nested Fields

Nested Fields

There are some settings that you add to your plugin that not all users need. To keep the user interface clean and organized for the majority of users you can nest and organized fields in a variety of ways.

The simplest method is to add an options panel. Here we will add an options panel to nest the price postfix in an expandable accordion.

$this->add_on->add_field( 'property_price', 'Property Price', 'text', null, 'Only digits, example: 435000' );

$this->add_on->add_options(
null,
'Price Settings',
array(
$this->add_on->add_field( 'property_price_postfix', 'Price Postfix', 'text', null, 'Example: Per Month' )
)
);

You can choose to add such an options panel attached to the price field.

$this->add_on->add_options(
$this->add_on->add_field( 'property_price', 'Property Price', 'text', null, 'Only digits, example: 435000' ),
'Price Settings',
array(
$this->add_on->add_field( 'property_price_postfix', 'Price Postfix', 'text', null, 'Example: Per Month' ),
$this->add_on->add_field( 'property_price_currency', 'Currency Symbol', 'text', null, 'Example: $, or €' )
)
);

Nested Radio Fields

You can also nest fields within the radio field. You can add as many fields as you like to the radio field』s options array.

$this->add_on->add_field(
'property_location',
'Property Location',
'radio',
array(
'enter_address' => array(
'Enter Address',
$this->add_on->add_field( 'property_address', 'Property Address', 'text' )
),
'enter_coordinates' => array(
'Enter Coordinates',
$this->add_on->add_field( 'property_coordinates', 'Property Coordinates', 'text' )
),
)
);

No Limits

You can even get a little crazy. There is no limit to how you can combine options panels and nested radio fields.

$this->add_on->add_field(
'location_settings',
'Property Location',
'radio',
array(
'search_by_address' => array(
'Search by Address',
$this->add_on->add_options(
$this->add_on->add_field( 'property_address','Property Address','text' ),
'Google Geocode API Settings',
array(
$this->add_on->add_field(
'geocode_method',
'Request Method',
'radio',
array(
'key_false' => array(
'No API Key',
'Limited number of requests.'
),
'key_true' => array(
'Google Developers API Key',
$this->add_on->add_field( 'api_key', 'API Key', 'text' ),
'Up to 2,500 requests per day and 5 requests per second.'
)
)
)
) // end Google Geocode API Settings fields
)
), // end Search by Address radio field
'enter_coordinates' => array(
'Enter Coordinates',
$this->add_on->add_field( 'property_latitude', 'Latitude', 'text' ),
$this->add_on->add_field( 'property_longitude', 'Longitude', 'text' )
)
)
);

Related

Add-On Structure

Text Fields

Radio Fields

Image Fields

Update Existing Posts

Update Existing Posts

WP All Import can import data to posts that already exist on your site, even if they were manually created instead of imported by WP All Import.

You need something in your import file that WP All Import can use to 「match」 the 「records」 in your import file to the posts that already exist on your site – that』s why it』s called Manual Record Matching.

When importing into existing records, you can specify which data WP All Import will update or overwrite, and which will be left alone.

Follow along with the below example to get a complete understanding of how to import data into existing posts on your site.

Example – Updating Multiple Property Listings With New Prices

I have a few property listings with outdated prices.

Example Properties

I have a CSV file with the MLS numbers of the properties, and the new prices.

CSV File

I』ve entered the MLS number of each property in my theme, so we can use the MLS number as the 「matcher」 so that WP All Import Import knows which price should be assigned to which property.

MLS Number

To do the import, upload your CSV in Step 1, and choose Existing Items.

Step 1 Existing Items

Continue to Step 2 and then to Step 3. In Step 3, set the price Custom Field to the price from your file.

Setting the price

The only field we want to import data to is the price. So just leave all the other fields blank. WP All Import Import will warn you that your post title and content are blank, but that』s fine – you can continue anyway. We don』t want to update the title or content here, just the price.

Now it』s time for the most important part – telling WP All Import Import how to match the records in our CSV file with the existing property listings already on our site. We』re going to match by the MLS number – since we have the MLS number in both places – on our site, and in our file.

Choose 「Match by Custom Field」 and click in the Name box to see a dropdown list of Custom Fields available to match by. Then choose 「mls_value」 – the Custom Field name the theme uses internally for the MLS field.

Record Matching

Then drag & drop the MLS column in your CSV file to the Value textbox.

MLS Value

Now, for each record in your file, WP All Import will look for a property on your site with an mls_value Custom Field that equals {mlsno[1]} from your file, and then import the price to it.

To ensure WP All Import only imports the price, and doesn』t overwrite the title, content, and other fields we left empty with blank values, we specify which data points we wanted updated, and which we want ignored.

Choose which data to update

Here』s the result after running the import – our 3 posts were updated with the new prices.

Import Summary

Data Updated

Here are the Hooks Provided By WP All Import and WP All Export

Here are the Hooks Provided By WP All Import and WP All Export

Our Action Reference provides hooks that allow you to change how WP All Import processes data. After customizing the examples based on your needs, you can place them either in WP All Import's Function Editor or your theme's functions.php file.

Click Here for more information.

pmxi_saved_post

This action fires when WP All Import saves a post of any type. The post ID, the record's data from your file, and a boolean value showing if the post is being updated are provided.

Parameters

ParamTypeDescription$post_idintThe ID of the item (post/user/taxonomy) saved or updated.$xml_nodeSimpleXMLElementThe libxml resource of the current XML element.$is_updateboolReturns 0 for new item 1 for updated item.

Usage

function my_saved_post( $post_id, $xml_node, $is_update ) {

// Retrieve the import ID.
$import_id = ( isset( $_GET['id'] ) ? $_GET['id'] : ( isset( $_GET['import_id'] ) ? $_GET['import_id'] : 'new' ) );

// Only run for import 8.
if ( $import_id == '8' ) {

// Convert SimpleXml object to array for easier use.
$record = json_decode( json_encode( ( array ) $xml_node ), 1 );

// Do something.

}

}
add_action( 'pmxi_saved_post', 'my_saved_post', 10, 3 );

Search the docs for pmxi_saved_post to see real-world code snippets that use this filter.

pmxi_before_xml_import

This action fires before any records are imported. It's used to call code that validates the import file or other code that needs to run at the start. It's called regardless of import file type used.

Parameters

ParamTypeDescription$import_idintThe ID of the import that's about to run.

Usage

function before_xml_import( $import_id ) {

// Only Run for import ID 5.
if ($import_id == 5) {

}

}
add_action('pmxi_before_xml_import', 'before_xml_import', 10, 1);

Search the docs for pmxi_before_xml_import to see real-world code snippets that use this filter.

pmxi_after_xml_import

This action fires after at the end of each import. The import object contains the values configured for the import and statistics for the last run.

Parameters

ParamTypeDescription$import_idintThe ID of the import that's about to run.$importobjectThe import object.

Usage

function after_xml_import( $import_id, $import ) {
// Only run if import ID is 5.
if ($import_id == 5) { // Do something. }
}
add_action( 'pmxi_after_xml_import', 'after_xml_import', 10, 2 );

Search the docs for pmxi_after_xml_import to see real-world code snippets that use this filter.

pmxi_update_post_meta

This action fires when WP All Import creates or updates post meta (custom fields). The post ID, field name, and field value are provided.

Parameters

ParamTypeDescription$post_idintThe ID of the imported item.$meta_keystring The name/key of the field that's being saved. $meta_valuemixed The value imported into the field.

Usage

function my_update_post_meta( $post_id, $meta_key, $meta_value) {

// Do something.

}

add_action( 'pmxi_update_post_meta', 'my_update_post_meta', 10, 3 );

Search the docs for pmxi_update_post_meta to see real-world code snippets that use this filter.

pmxi_gallery_image

This action fires each time an image is imported. It provides the post ID, image attachment ID, and image filepath as parameters. Whether or not existing images are deleted before new ones are imported is also indicated.

Parameters

ParamTypeDescription$post_idintThe ID of the imported item.$att_idintThe ID of the image that was imported.$filepathstringThe local file path to the full size image.$is_keep_existing_imagesbool/emptyReturns 1 if keeping existing images. Always cast as empty in the function arguments.

Usage

function wpai_image_imported( $post_id, $att_id, $filepath, $is_keep_existing_images = '' ) {

// Do something.

}
add_action( 'pmxi_gallery_image', 'wpai_image_imported', 10, 4 );

Search the docs for pmxi_gallery_image to see real-world code snippets that use this filter.

pmxi_attachment_uploaded

This action fires when an attachment is uploaded. It provides the post ID, attachment ID, and attachment file path. If you need to work with images use pmxi_gallery_image.

Parameters

ParamTypeDescription$post_idintThe post ID that's being imported.$att_idintThe attachment ID.$filepathstringThe full path to the file: C:pathtowordpresswp-contentuploads20105filename.png

Usage

function my_attachment_uploaded($pid, $attid, $filepath){
$attachment = get_post($attid);
// Do something with $attachment.
}add_action('pmxi_attachment_uploaded', 'my_attachment_uploaded', 10, 3);

Search the docs for pmxi_attachment_uploaded to see real-world code snippets that use this filter.

wp_all_import_is_post_to_update

This filter is used to determine if a post should be updated or skipped. It takes the post ID and, optionally, an array of XML nodes. The returned value should be either true to update the post or false to skip it.

Parameters

ParamTypeDescription$continuebooltrue to update, false to skip.$post_idintThe Post ID that's about to be updated.$xml_nodearrayAn array containing the data for the current import record.$import_idintThe ID of the import that's running.

Usage

function my_is_post_to_update( $continue, $post_id, $xml_node, $import_id ) {
// Run this code on a specific import
if ($import_id === 5) {
// Do something to decide if this post should update
if ( ... ) {
return true;
}
// Don't update this post
return false;
}
}

add_filter( 'wp_all_import_is_post_to_update', 'my_is_post_to_update', 10, 4 );

Search the docs for wp_all_import_is_post_to_update to see real-world code snippets that use this filter.

pmxi_acf_custom_field

This filter allows the modification of ACF field values. Only fields set to be updated or created during the import will fire this hook.

Parameters

ParamTypeDescription$valuemixedThe value being imported to the ACF field.$post_idintThe Post ID that's being imported.$namestringThe ACF field name.

Usage

function wp_all_import_pmxi_acf_custom_field( $value, $pid, $name ) {
// Code here.
}add_filter( 'pmxi_acf_custom_field', 'wp_all_import_pmxi_acf_custom_field', 10, 3 );

Search the docs for pmxi_acf_custom_field to see real-world code snippets that use this filter.

pmxi_after_post_import

This action fires after each imported post. If you need post IDs you should use pmxi_saved_post.

Parameters

ParamTypeDescription$import_idintThe import ID that's running.

Usage

function my_after_post_import($import_id)
{
// Only run for import ID 5.
if ($import_id === 5) { // Do something.
}
}
add_action('pmxi_after_post_import', 'my_after_post_import', 10, 1);

Search the docs for pmxi_after_post_import to see real-world code snippets that use this filter.

pmxi_article_data

This filter allows updating core post fields before each post is imported. When updating, $post_to_update contains the current post's WP_Post object. The current file record's data is in $current_xml_node.

Parameters

ParamTypeDescription$articleDataarrayCurrent article data.$importobjectThe import object.$post_to_updateobjectPost object for the post that's being updated.$current_xml_nodearrayParsed data for the current import record.

Here are the fields available for modification in the $articleData array:

array(15) { ["post_type"]=>string ["post_status"]=>string ["comment_status"]=>string ["ping_status"]=>string ["post_title"]=>string ["post_excerpt"]=>string ["post_name"]=>string ["post_content"]=>string ["post_date"]=>string ["post_date_gmt"]=>string ["post_author"]=>int ["menu_order"]=>int ["post_parent"]=>int ["page_template"]=>string ["ID"]=>string }

Usage

function my_pmxi_article_data( $articleData, $import, $post_to_update, $current_xml_node ) {
// Do something with the article data.
return $articleData;
}
add_filter('pmxi_article_data', 'my_pmxi_article_data', 10, 4);

Search the docs for pmxi_article_data to see real-world code snippets that use this filter.

pmxi_before_delete_post

This action fires just before WP All Import deletes a post. The post ID and import object are provided as parameters.

Parameters

ParamTypeDescription$post_idintThe post ID that's about to be deleted.$importobjectThe import object.

Usage

function before_delete_post( $post_id, $import ) {

// do something before post is deleted

}

add_action('pmxi_before_delete_post', 'before_delete_post', 10, 2);

Search the docs for pmxi_before_delete_post to see real-world code snippets that use this filter.

pmxi_custom_field

This filter first before a custom field's value is saved. If the field is being updated the $original_value is provided. The $value is provided and must be returned. The field's name, $key, and the post being updated, $post_id, are available.

Parameters

ParamTypeDescription$valuestringThe value being imported into the custom field.$post_idintThe post ID.$keystringThe custom field key.$original_valuestringOriginal, unserialized, value.$existing_meta_keysarrayExisting meta keys.$import_idintThe import ID that's running.

Usage

function my_custom_field( $value, $post_id, $key, $original_value, $existing_meta_keys, $import_id ) {

// Only run for import ID 5.
if ($import_id === 5) {

// Only modify the 'myField' custom field for post 65.
if( $key == 'myField' && $post_id == 65 ){

// Do something.

}

}

// Return the value for the custom field.
return $value;
}

add_filter( 'pmxi_custom_field', 'my_custom_field', 10, 6 );

Search the docs for pmxi_custom_field to see real-world code snippets that use this filter.

pmxi_delete_post

This action fires just before posts are deleted at the end of an import when using the 'Delete products that are no longer present in your file' option. It allows performing actions on the posts immediately before they are deleted. The list of post IDs to be deleted and the import object are provided.

Parameters

ParamTypeDescription$idsarrayArray of post IDs that will be deleted.$importobjectThe import object.

Usage

function wpai_pmxi_delete_post( $ids = array(), $import ){

foreach( $ids as $pid ) {

// do something with post using ID - $pid

}
}
add_action( 'pmxi_delete_post', 'wpai_pmxi_delete_post', 10, 2 );

Search the docs for pmxi_delete_post to see real-world code snippets that use this filter.

pmxi_single_category

This filter fires before taxonomy terms are imported and can be used to filter them. An array containing the term information and the taxonomy name are provided.

Parameters

ParamTypeDescription$term_intoarrayArray with term information.$tax_namestringThe taxonomy name.

Here is the $term_into array:

Array
(
[name] => {string}
[parent] => {int or empty if none}
[assign] => {bool}
[is_mapping] => {1 if true, empty if false}
[hierarchy_level] => {int}
[max_hierarchy_level] => {int}
)

Usage

function wpai_pmxi_single_category( $term_into, $tax_name ) {

// Do something.

// Return the updated term information.
return $term_into;

}

add_filter( 'pmxi_single_category', 'wpai_pmxi_single_category', 10, 2 );

Search the docs for pmxi_single_category to see real-world code snippets that use this filter.

wp_all_import_attachments_uploads_dir

DescriptionCan be used to change the directory that imported attachments are saved to.

Parameters

ParamTypeDescription$uploadsarrayContains information related to the WordPress uploads path & URL.$articleDataarrayContains a list of data related to the post/user/taxonomy being imported.$current_xml_nodearrayArray of data for current import record.$import_idintThe import ID that's running.

Usage

add_filter( 'wp_all_import_attachments_uploads_dir', 'wpai_wp_all_import_attachments_uploads_dir', 10, 4 );
function wpai_wp_all_import_attachments_uploads_dir( $uploads, $articleData, $current_xml_node, $import_id ) {
return $uploads;
}

Search the docs for wp_all_import_attachments_uploads_dir to see real-world code snippets that use this hook.

wp_all_import_auto_create_csv_headers

DescriptionAuto generate headers, or use headers in file.

You cannot call this hook from WP All Import's Function Editor. It must be saved in a plugin or in your theme.

Parameters

ParamTypeDescription$create_headersboolAuto-generate headers? true / false.$import_idintThe import ID that's running.

Usage

add_filter( 'wp_all_import_auto_create_csv_headers', 'wpai_wp_all_import_auto_create_csv_headers', 10, 2 );
function wpai_wp_all_import_auto_create_csv_headers( $create_headers, $import_id ){
// Return true to auto-generate header, or false to use the header in the file.
return $create_headers;
}

Search the docs for wp_all_import_auto_create_csv_headers to see real-world code snippets that use this hook.

wp_all_import_copy_uploaded_file_into_files_folder

DescriptionReturn TRUE and WP All Import will copy uploaded import file into /wp-content/uploads/wpallimport/files folder.

Parameters

ParamTypeDescription$copy_uploaded_filesboolCopy file? true / false.

Usage

function wpai_wp_all_import_copy_uploaded_file_into_files_folder( $copy_uploaded_files ) {
return $copy_uploaded_files;
}
add_filter( 'wp_all_import_copy_uploaded_file_into_files_folder', 'wpai_wp_all_import_copy_uploaded_file_into_files_folder', 10, 1 );

Search the docs for wp_all_import_copy_uploaded_file_into_files_folder to see real-world code snippets that use this hook.

wp_all_import_curl_download_only

DescriptionForce WP All Import to download the feed on the first request (skips reading headers). Useful if feeds reject multiple pings/requests.

This code must be added in a plugin or your theme. It will not work in WP All Import's Function Editor.

Parameters

ParamTypeDescription$download_onlyboolOnly download via curl? true / false.

Usage

add_filter( 'wp_all_import_curl_download_only', 'wpai_wp_all_import_curl_download_only', 10, 1 );

function wpai_wp_all_import_curl_download_only( $download_only ){
return true;
}

Search the docs for wp_all_import_curl_download_only to see real-world code snippets that use this hook.

wp_all_import_feed_type

DescriptionCan be used to define the feed type (xml/csv/json/etc).

This code must be added in a plugin or your theme. It will not work in WP All Import's Function Editor.

Parameters

ParamTypeDescription$typestringThe feed type.$urlstringThe URL that's being imported.

Usage

add_filter( 'wp_all_import_feed_type', 'wpai_feed_type', 10, 2 );
function wpai_feed_type( $type, $url ){
// Check $url and return $type.
return $type;
}

Search the docs for wp_all_import_feed_type to see real-world code snippets that use this hook.

wp_all_import_get_image_from_gallery

DescriptionCalled after an existing image is found in the Media Library. Only works with the "Use images currently in Media Library" and "Use images currently uploaded in wp-content/uploads/wpallimport/files/" options.

Parameters

ParamTypeDescription$attachobjectThe existing image that will be attached.$image_namestringThe image name that will be imported.$targetDirstringThe directory that the image will be uploaded to.

Usage

add_filter( 'wp_all_import_get_image_from_gallery', 'wpai_wp_all_import_get_image_from_gallery', 10, 3 );
function wpai_wp_all_import_get_image_from_gallery( $attach, $image_name, $targetDir ) {
/**
* do something with found attachment
*/
return $attach; // if attach === false the image will be downloaded
}

Search the docs for wp_all_import_get_image_from_gallery to see real-world code snippets that use this hook.

wp_all_import_handle_upload

DescriptionFilters the data array for attachments & images uploaded through WP All Import.

Parameters

ParamTypeDescription$filearrayContains the filepath, URL & filetype

Usage

add_filter( 'wp_all_import_handle_upload', 'wpai_wp_all_import_handle_upload', 10, 1 );
function wpai_wp_all_import_handle_upload( $file ){
// Handle & return $file here.
}

Search the docs for wp_all_import_handle_upload to see real-world code snippets that use this hook.

wp_all_import_image_filename

DescriptionCan be used to set a custom filename for an imported image.

Parameters

ParamTypeDescription$filenamestringThe filename as created by the import.$img_titlestringFrom "SEO and advanced options". Will always be blank for ACF fields.$img_captionstringFrom "SEO and advanced options". Will always be blank for ACF fields.$img_altstringFrom "SEO and advanced options". Will always be blank for ACF fields.$articleDataarrayArray of current import item data.$import_idintThe import ID.$img_urlstringThe image URL.

Usage

function wpai_image_filename( $filename = '', $img_title = '', $img_caption = '', $img_alt = '', $articleData = '', $import_id = '', $img_url = '' ) {
// Your code
return $filename;
}
add_filter( 'wp_all_import_image_filename', 'wpai_image_filename', 10, 7 );

Search the docs for wp_all_import_image_filename to see real-world code snippets that use this hook.

wp_all_import_images_uploads_dir

DescriptionCan be used to set a custom path in which images (as well as media intended for ACF fields) are uploaded. Only applies to media uploaded via WP All Import.

Parameters

ParamTypeDescription$uploadsarrayContains information related to the WordPress uploads path & URL$articleDataarrayContains a list of data related to the post/user/taxonomy being imported$current_xml_nodearrayContains a list of nodes within the current import record$import_idintContains the ID of the import.

Usage

add_filter( 'wp_all_import_images_uploads_dir', 'wpai_wp_all_import_images_uploads_dir', 10, 4 );

function wpai_wp_all_import_images_uploads_dir( $uploads, $articleData, $current_xml_node, $import_id ) {
// Do something with code here.
return $uploads;
}

Search the docs for wp_all_import_images_uploads_dir to see real-world code snippets that use this hook.

wp_all_import_is_check_duplicates

DescriptionTurn duplicate checking on/off. If you turn it off, WP All Import will create duplicate items each re-run.

Parameters

ParamTypeDescription$is_check_duplicatesboolCheck for duplicates? true / false.$import_idintThe import ID.

Usage

add_filter( 'wp_all_import_is_check_duplicates', 'wpai_is_check_duplicates', 10, 2 );

function wpai_is_check_duplicates( $is_check_duplicates, $import_id ) {
// Cancel checking for duplicates if this is import ID 10.
return ( $import_id == 10 ) ? false : true;
}

Search the docs for wp_all_import_is_check_duplicates to see real-world code snippets that use this hook.

wp_all_import_is_php_allowed

DescriptionDetermine whether PHP functions in the import template will be executed.

Parameters

ParamTypeDescription$is_php_allowedboolExecute functions? true / false.

Usage

function wpai_wp_all_import_is_php_allowed( $is_php_allowed ) {
return $is_php_allowed;
}
add_filter( 'wp_all_import_is_php_allowed', 'wpai_wp_all_import_is_php_allowed', 10, 1 );

Search the docs for wp_all_import_is_php_allowed to see real-world code snippets that use this hook.

wp_all_import_is_post_to_create

DescriptionThis filter is used to determine if a post should be created or skipped. The returned value should be either true to create the post or false to skip it.

Parameters

ParamTypeDescription$continue_importboolCreate post? true / false.$dataarrayAn array holding values for the current record. If importing from XML, attributes can be accessed as SimpleXMLElement objects.$import_idintThe import ID.

Usage

function my_is_post_to_create( $continue_import, $data, $import_id ) {
// Unless you want this code to execute for every import, check the import id
// if ($import_id === 5) { ... }
return true;
}
add_filter( 'wp_all_import_is_post_to_create', 'my_is_post_to_create', 10, 3 );

Search the docs for wp_all_import_is_post_to_create to see real-world code snippets that use this hook.

wp_all_import_is_post_to_delete

DescriptionThis fires when a missing post is about to be deleted by an import.

Parameters

ParamTypeDescription$is_post_to_deleteboolDelete post? true / false.$post_idintThe post ID that's about to be deleted.$importobjectThe import object.

Usage

function my_is_post_to_delete( $is_post_to_delete, $post_id, $import ) {
// Unless you want this code to execute for every import, check the import id
// if ( $import->id == 5 ) { ... }
return true;
}
add_filter( 'wp_all_import_is_post_to_delete', 'my_is_post_to_delete', 10, 3 );

Search the docs for wp_all_import_is_post_to_delete to see real-world code snippets that use this hook.

wp_all_import_is_unlink_missing_posts

DescriptionUsed to completely remove the relationship between import and post from pmxi_posts database table.

Parameters

ParamTypeDescription$is_unlink_missingboolUnlink post? true / false.$import_idintThe import ID.$post_idintThe post ID.

Usage

add_filter( 'wp_all_import_is_unlink_missing_posts', 'wpai_wp_all_import_is_unlink_missing_posts', 10, 3 );

function wpai_wp_all_import_is_unlink_missing_posts( $is_unlink_missing, $import_id, $post_id ) {
// Do something with code here.
return $is_unlink_missing;
}

Search the docs for wp_all_import_is_unlink_missing_posts to see real-world code snippets that use this hook.

wp_all_import_multi_glue

DescriptionChange the delimiter used when querying multiple values with XPath (default is ", "). Learn about XPath queries here: XPath Queries.

Parameters

ParamTypeDescription$delimiterstringThe delimiter.

Usage

add_filter( 'wp_all_import_multi_glue', 'wpai_wp_all_import_multi_glue', 10, 1 );
function wpai_wp_all_import_multi_glue( $delimiter ) {
// change delimiter if you want.
// e.g.:
// $delimiter = '|';
return $delimiter;
}

Search the docs for wp_all_import_multi_glue to see real-world code snippets that use this hook.

wp_all_import_phpexcel_object

DescriptionAccess/modify the PHPExcel object and file path for the import file.

Parameters

ParamTypeDescription$PHPExcelobjectThe PHPExcel object.$xlsFilePathstringThe path to the import file.

Usage

add_filter( 'wp_all_import_phpexcel_object', 'wpai_wp_all_import_phpexcel_object', 10, 2 );
function wpai_wp_all_import_phpexcel_object( $PHPExcel, $xlsFilePath ) {
return $PHPExcel;
}

Search the docs for wp_all_import_phpexcel_object to see real-world code snippets that use this hook.

wp_all_import_search_image_by_wp_attached_file

DescriptionCan be used to stop WP All Import from looking for existing images via _wp_attached_file.

Parameters

ParamTypeDescription$is_search_by_wp_attached_fileboolLook in _wp_attached_file? true / false.

Usage

add_filter( 'wp_all_import_search_image_by_wp_attached_file', 'wpai_wp_all_import_search_image_by_wp_attached_file', 10, 1 );
function wpai_wp_all_import_search_image_by_wp_attached_file( $is_search_by_wp_attached_file ) {
// do something with code here
return $is_search_by_wp_attached_file;
}

Search the docs for wp_all_import_search_image_by_wp_attached_file to see real-world code snippets that use this hook.

wp_all_import_set_post_terms

DescriptionCalled when WP All Import is setting the post taxonomy terms.

Parameters

ParamTypeDescription$term_taxonomy_idsarrayArray of taxonomy IDs.$tx_namestringThe name of the taxonomy.$pidintThe post ID.$import_idintThe import ID.

Usage

add_filter( 'wp_all_import_set_post_terms', 'wpai_wp_all_import_set_post_terms', 10, 4 );

function wpai_wp_all_import_set_post_terms( $term_taxonomy_ids, $tx_name, $pid, $import_id ) {
// Code here.
return $term_taxonomy_ids;
}

Search the docs for wp_all_import_set_post_terms to see real-world code snippets that use this hook.

wp_all_import_skip_x_csv_rows

DescriptionSpecifies the amount of rows to be skipped. You must use this hook in a plugin or your theme, not in WP All Import's Function Editor.

Parameters

ParamTypeDescription$skip_rowsbool/intReturn false to not skip rows. Return integer to specify how many rows to skip.$import_idintThe import ID.

Usage

add_filter( 'wp_all_import_skip_x_csv_rows', 'wpai_skip_rows', 10, 2 );

function wpai_skip_rows( $skip_rows, $import_id ) {
// Handle & return $skip_rows here.
}

Search the docs for wp_all_import_skip_x_csv_rows to see real-world code snippets that use this hook.

wp_all_import_specified_records

DescriptionUse this to override the specified records to be imported. It's a comma separated list of ints or int ranges. e.g. "1-7,10,15-20". The integers represent the position in the data file.

Parameters

ParamTypeDescription$specified_recordsstringThe current value from the "Import only specified records" option.$import_idintThe import ID.$root_nodes???

Usage

function my_specified_records( $specified_records, $import_id, $root_nodes ) {
// Unless you want this code to execute for every import, check the import id
// if ($import_id === 5) { ... }
// your code here
return $specified_records;
}

add_filter( 'wp_all_import_specified_records', 'my_specified_records', 10, 3 );

Search the docs for wp_all_import_specified_records to see real-world code snippets that use this hook.

wp_all_import_use_wp_set_object_terms

DescriptionForce WP All Import to use the wp_set_object_terms() WordPress function. Using this function is a bit slower, but it's sometimes necessary for compatibility with certain themes and plugins.

Parameters

ParamTypeDescription$use_wp_set_object_termsboolReturn true to use WordPress function wp_set_object_terms(). Return false to use pure SQL.$tx_namestringThe taxonomy name.

Usage

function wpai_wp_all_import_use_wp_set_object_terms( $use_wp_set_object_terms, $tx_name ) {
return $use_wp_set_object_terms;
}

add_filter( 'wp_all_import_use_wp_set_object_terms', 'wpai_wp_all_import_use_wp_set_object_terms', 10, 2 );

Search the docs for wp_all_import_use_wp_set_object_terms to see real-world code snippets that use this hook.

wpallimport_after_images_import

DescriptionCalled after the images section of the import is processed, but only when the "Keep images currently in Media Library" option is enabled.

Parameters

ParamTypeDescription$post_idintThe post ID.$gallery_attachment_idsarrayAn array of the images that were imported to the post.$missing_imagesarrayAn array of the images that were missing, based on previous imports.

Usage

function soflyy_img_import( $post_id, $gallery_attachment_ids, $missing_images ) {
// do something
}

add_action( 'wpallimport_after_images_import', 'soflyy_img_import', 10, 3 );

Search the docs for wpallimport_after_images_import to see real-world code snippets that use this hook.

wpallimport_xml_row

DescriptionAllows reading or modification of the data record before importing. All file types (CSV/JSON/XLS/XLSX/TXT/etc) are converted to XML, so this hook works with all of them.

Please note that XML nodes generated by this hook can't be used in "FOREACH" ACF Repeater fields.

Parameters

ParamTypeDescription$xml_nodelibxml resourceSimpleXMLElement containing the current records data.

Usage

function wpai_xml_row( $xml_node ) {
// Modify simpleXML object as needed
return $xml_node;
}
add_filter( 'wpallimport_xml_row', 'wpai_xml_row', 10, 1 );

Search the docs for wpallimport_xml_row to see real-world code snippets that use this hook.

pmxi_product_variation_saved

This action fires each time WP All Import save a product variation. The variation's ID is provided.

Parameters

ParamTypeDescription$variation_idintThe variation ID.

Usage

function wpai_wp_all_import_variation_imported( $variation_id ){

// Do something.

}

add_action( 'pmxi_product_variation_saved', 'wpai_wp_all_import_variation_imported', 10, 1 );

Search the docs for pmxi_product_variation_saved to see real-world code snippets that use this filter.

wp_all_import_get_prices_from_first_variation

DescriptionCan be used to set the parent product price to the price of the first variation in the case that WP All Import converts the product to a simple product.

Parameters

ParamTypeDescription$get_prices_from_first_variationboolSet parent product price to first variation price? true / false.$product_idintThe product ID.$import_idintThe import ID.

Usage

function wpai_wp_all_import_get_prices_from_first_variation( $get_prices_from_first_variation, $product_id, $import_id ) {
// do something
return $get_prices_from_first_variation;
}

add_filter( 'wp_all_import_get_prices_from_first_variation', 'wpai_wp_all_import_get_prices_from_first_variation', 10, 3 );

Search the docs for wp_all_import_get_prices_from_first_variation to see real-world code snippets that use this hook.

wp_all_import_make_product_simple

DescriptionCalled after a product is converted into a Simple Product by our WooCommerce Add-On. This only happens when the "Create products with no variations as simple product" option is enabled.

Parameters

ParamTypeDescription$product_idintThe product ID.$import_idintThe import ID.

Usage

function wpai_wp_all_import_make_product_simple( $product_id, $import_id ) {
// Code here.
}

add_action( 'wp_all_import_make_product_simple', 'wpai_wp_all_import_make_product_simple', 10, 2 );

Search the docs for wp_all_import_make_product_simple to see real-world code snippets that use this hook.

wp_all_import_product_attributes_delimiter

DescriptionModify the delimiter used to separate multiple attributes in the WooCommerce Add-On. Default is the pipe symbol "|".

Parameters

ParamTypeDescription$delimiterstringDefault is "|".$product_idintThe product ID.$import_idintThe import ID.

Usage

function wpai_wp_all_import_product_attributes_delimiter( $delimiter, $product_id, $import_id ) {
return $delimiter;
}

add_filter( 'wp_all_import_product_attributes_delimiter', 'wpai_wp_all_import_product_attributes_delimiter', 10, 3 );

Search the docs for wp_all_import_product_attributes_delimiter to see real-world code snippets that use this hook.

wp_all_import_variable_product_imported

DescriptionCalled when WP All Import saves a variable product

Parameters

ParamTypeDescription$product_idintThe product ID.

Usage

add_action( 'wp_all_import_variable_product_imported', 'wpai_wp_all_import_variable_product_imported', 10, 1 );

function wpai_wp_all_import_variable_product_imported( $post_parent ) {
// Code here.
}

Search the docs for wp_all_import_variable_product_imported to see real-world code snippets that use this hook.

wp_all_import_variation_any_attribute

DescriptionReturn true to set variation as "Any" in the case that multiple attribute values are present (e.g.: L|XL|XXL).

Parameters

ParamTypeDescription$variation_any_attributeboolSet variation to "Any"? true / false.$import_idintThe import ID.

Usage

function wpai_wp_all_import_variation_any_attribute( $variation_any_attribute, $import_id ) {
return $variation_any_attribute;
}

add_filter( 'wp_all_import_variation_any_attribute', 'wpai_wp_all_import_variation_any_attribute', 10, 2 );

Search the docs for wp_all_import_variation_any_attribute to see real-world code snippets that use this hook.

wp_all_import_variation_taxonomies

DescriptionCan be used to add taxonomies to WooCommerce product variations, which isn't supported by WooCommerce out of the box.

Parameters

ParamTypeDescription$taxonomiesarrayList of taxonomies.

Usage

add_filter( 'wp_all_import_variation_taxonomies', 'wpai_wp_all_import_variation_taxonomies', 10, 1 );

function wpai_wp_all_import_variation_taxonomies( $taxonomies ){
if ( ! in_array( 'my_taxonomy_name', $taxonomies ) ) $taxonomies[] = 'my_taxonomy_name';
return $taxonomies;
}

Search the docs for wp_all_import_variation_taxonomies to see real-world code snippets that use this hook.

pmxe_after_export

This action is used to run code after the export has completed. It provides the export ID and an object containing stats, settings, data, etc for the export.

Parameters

ParamTypeDescription$export_idintThe export ID.$exportObjobjectThe export object.

Usage

function wp_all_export_after_export( $export_id, $exportObj ) {
// Run for export 5 only
if ( $export_id === 5 ) {
        // Do something
}
}
add_action( 'pmxe_after_export', 'wp_all_export_after_export', 10, 2 );

Search the docs for pmxe_after_export to see real-world code snippets that use this filter.

pmxe_after_iteration

This action fires after each iteration only when running via cron. It provides the export ID and an object containing stats, settings, data, etc for the export.

Parameters

ParamTypeDescription$export_idintThe export ID.$exportObjobjectThe export object.

Usage

function wpae_continue_cron( $export_id, $exportObj ) {
// Run this for a specific export
if ($export_id === 5) { // Do something }
} add_action( 'pmxe_after_iteration', 'wpae_continue_cron', 10, 2 );

Search the docs for pmxe_after_iteration to see real-world code snippets that use this filter.

pmxe_before_export

This action runs at the start of an export before any data is processed. It provides the export ID and must be called outside of the Function Editor - such as from your theme's functions.php file.

Parameters

ParamTypeDescription$export_idintThe export ID.

Usage

function wp_all_export_before_export( $export_id ) {
// Run this for a specific export.
if ($export_id === 5) { // Do something.
}
} add_action( 'pmxe_before_export', 'wp_all_export_before_export', 10, 1 );

Search the docs for pmxe_before_export to see real-world code snippets that use this filter.

pmxe_exported_post

This action fires after each record is saved to the export file. It provides the post ID and an object containing stats, settings, data, etc for the export.

Parameters

ParamTypeDescription$post_idintThe post ID.$exportObjobjectThe export object.

Usage

function wpae_pmxe_exported_post( $post_id, $exportObject ) { // Only run for a certain post.
if( $post_id == 55 ){ // Do something.
}}
add_action('pmxe_exported_post', 'wpae_pmxe_exported_post', 10, 2);

Search the docs for pmxe_exported_post to see real-world code snippets that use this filter.

pmxe_woo_field

This filter allows WooCommerce field values to be modified as they're exported. It provides the field's value, name, and the post ID. You must return the value as a string.

Parameters

ParamTypeDescription$valuemixedThe value of the field.$field_namestringThe field name.$pidintThe post ID.

Usage

function wp_all_export_woo_field( $value, $field_name, $pid ) { if( $field_name == '_a_field' ){
// Generate the new value
return $new_value; } else { return $value; }
}add_filter( 'pmxe_woo_field', 'wp_all_export_woo_field', 10, 3 );

Search the docs for pmxe_woo_field to see real-world code snippets that use this filter.

wp_all_export_additional_data

This filter only fires when Simple XML Feed exports run. It does nothing for any other export type. Any additional fields currently added will be in the $add_data parameter. The $options parameter contains the full export configuration. The export ID is provided in the last parameter.

Parameters

ParamTypeDescription$add_dataarrayAny additional data to add, empty by default.$optionsarrayExport options array containing the export's configuration.$export_idintID of the running export.

Usage

function wpae_additional_data( $add_data, $options, $export_id ) {

// Add data as needed.

// Return the updated data array.
return $add_data;

}

add_filter( 'wp_all_export_additional_data', 'wpae_additional_data', 10, 2 );

Search the docs for wp_all_export_additional_data to see real-world code snippets that use this filter.

wp_all_export_after_csv_line

This filter fires after each CSV line and provides the file resource handle so that additional characters can be added. The second parameter provides the export ID.

Parameters

ParamTypeDescription$streamfile system pointer resourceThe resource handle pointing to the export file.$export_idintThe export ID.

Usage

function wpae_wp_all_export_after_csv_line( $stream, $export_id ){

// Do something.

return $stream;

}

add_filter( 'wp_all_export_after_csv_line', 'wpae_wp_all_export_after_csv_line', 10, 2 );

Search the docs for wp_all_export_after_csv_line to see real-world code snippets that use this filter.

wp_all_export_config_options

This filter fires during exports and was specifically added to allow changing the max_execution_time and max_input_time values. The other options are listed below but should not be modified with the filter.

Parameters

ParamTypeDescription$optionsarrayExport configuration options.

Option array values:

Array
(
[info_api_url] => http://www.wpallimport.com
[scheduling_license] => {hashed string}
[scheduling_license_status] => {status string}
[dismiss] => 0
[dismiss_manage_top] => 0
[dismiss_manage_bottom] => 0
[cron_job_key] => {string}
[max_input_time] => -1
[max_execution_time] => -1
[secure] => 1
[license] => {hashed string}
[license_status] => {status string}
[zapier_api_key] => {string}
[zapier_invitation_url] =>
[zapier_invitation_url_received] =>
)

Usage

function wpai_wp_all_import_config_options( $options ) {

// Do something.

return $options;

}

add_filter( 'wp_all_export_config_options', 'wpai_wp_all_import_config_options', 10, 1 );

Search the docs for wp_all_export_config_options to see real-world code snippets that use this filter.

wp_all_export_csv_headers

DescriptionManipulate export file headers.

Parameters

ParamTypeDescription$headersarrayCurrent headers.$export_idintThe export ID.

Usage

add_filter( 'wp_all_export_csv_headers', 'wpae_wp_all_export_csv_headers', 10, 2 );

function wpae_wp_all_export_csv_headers( $headers, $export_id ) {
// Code here.
return $headers;
}

Search the docs for wp_all_export_csv_headers to see real-world code snippets that use this hook.

wp_all_export_csv_rows

DescriptionFilters the records to export. This is for CSV formatted exports only. See 'wp_all_export_xml_rows' for filtering XML exports.

Parameters

ParamTypeDescription$articlesarrayAn array of records for exporting. Each article is keyed by the column header name for that field.$optionsarrayThe export configuration options.$export_idintThe export ID.

Usage

function wp_all_export_csv_rows( $articles, $options, $export_id ) {
// Unless you want this code to execute for every export, be sure to check the export id
// if ($export_id == 5) { ...
// Loop through the array and unset() any entries you don't want exported
// foreach ($articles as $key => $article) {
// if ($article["Title"] == "Something") {
// unset($articles[$key]);
// }
// }
return $articles; // Return the array of records to export
}
add_filter( 'wp_all_export_csv_rows', 'wp_all_export_csv_rows', 10, 3 );

Search the docs for wp_all_export_csv_rows to see real-world code snippets that use this hook.

wp_all_export_export_file_name

DescriptionFilter to change the filename of the exported data file

Parameters

ParamTypeDescription$file_path stringThe file path, including the filename.$export_id intThe export ID.

Usage

function wpae_wp_all_export_export_file_name( $file_path, $export_id ){
// Unless you want your code to execute for every export, be sure to check the export id
// if ($export_id === 5) { ... }

return $file_path;
}

add_filter( 'wp_all_export_export_file_name', 'wpae_wp_all_export_export_file_name', 10, 2 );

Search the docs for wp_all_export_export_file_name to see real-world code snippets that use this hook.

wp_all_export_generate_bundle

DescriptionDetermine whether the bundle zip file should be generated.

Parameters

ParamTypeDescription$is_generate_bundle boolGenerate bundle? true / false.

Usage

add_filter( 'wp_all_export_generate_bundle', 'wpae_do_not_generate_bundle', 10, 1 );
function wpae_do_not_generate_bundle( $is_generate_bundle ) {
return $is_generate_bundle ;
}

Search the docs for wp_all_export_generate_bundle to see real-world code snippets that use this hook.

wp_all_export_implode_delimiter

DescriptionModify the implode delimiter for export fields. Default delimiter is the pipe symbol "|".

Parameters

ParamTypeDescription$implode_delimiterstringThe delimiter to be used. Default is "|".$export_idintThe export ID.

Usage

function wpae_wp_all_export_implode_delimiter( $implode_delimiter, $export_id ) {
// do something
return $implode_delimiter;
}

add_filter( 'wp_all_export_implode_delimiter', 'wpae_wp_all_export_implode_delimiter', 10, 2 );

Search the docs for wp_all_export_implode_delimiter to see real-world code snippets that use this hook.

wp_all_export_is_csv_headers_enabled

DescriptionCan be used to completely remove the CSV header.

Parameters

ParamTypeDescription$is_headers_enabled boolKeep headers? true / false.$export_idintThe export ID.

Usage

add_filter( 'wp_all_export_is_csv_headers_enabled', 'wpae_wp_all_export_is_csv_headers_enabled', 10, 2 );

function wpae_wp_all_export_is_csv_headers_enabled( $is_headers_enabled, $export_id ){
// return 'false' to remove the header.
return $is_headers_enabled;
}

Search the docs for wp_all_export_is_csv_headers_enabled to see real-world code snippets that use this hook.

wp_all_export_order_item

DescriptionFilters the items in a WooCommerce Order to export.

Parameters

ParamTypeDescription$is_export_recordboolInclude item in export? true / false.$product_idintThe product ID.$export_optionsarrayThe export options array.$export_idintThe export ID.

Usage

function filter_my_order_items ( $is_export_record, $product_id, $export_options, $export_id ) {
// Unless you want this to execute for every export you should check the id here:
// if ( $export_id === 5 ) {
// Your code here
return true;
}
add_filter( "wp_all_export_order_item", "filter_my_order_items", 10, 4 );

Search the docs for wp_all_export_order_item to see real-world code snippets that use this hook.

wp_all_export_pre_csv_headers

DescriptionAllows for CSV headers to be added above the default headers.

Parameters

ParamTypeDescription$preCsvHeadersbool / stringThe headers to add to the file. Default false.$export_idintThe export ID.

Usage

add_filter( 'wp_all_export_pre_csv_headers', 'export_add_headers', 10, 2 );

function export_add_headers ( $preCsvHeaders, $exportId ) {
// Modify & return $preCsvHeaders
return $preCsvHeaders;
}

Search the docs for wp_all_export_pre_csv_headers to see real-world code snippets that use this hook.

wp_all_export_product_variation_mode

DescriptionChoose whether to export parent products or just variations.

Parameters

ParamTypeDescription$exportVariationMode stringExport variation mode.$export_idintThe export ID.

Usage

add_filter( 'wp_all_export_product_variation_mode', 'export_only_variations', 10, 2 );
function export_only_variations( $exportVariationMode, $exportID ) {
return XmlExportEngine::VARIABLE_PRODUCTS_EXPORT_VARIATION; // Only export variations for variable products, don't export the parent products
}

Search the docs for wp_all_export_product_variation_mode to see real-world code snippets that use this hook.

wp_all_export_raw_prices

DescriptionCan be used to disable price formatting for WooCommerce exports.

Parameters

ParamTypeDescription$raw_pricesboolDisable price formatting? true / false.

Usage

add_filter( 'wp_all_export_raw_prices', 'my_disable_formatting', 10, 1 );

function my_disable_formatting( $raw_prices ) {
// do something
return $raw_prices;
}

Search the docs for wp_all_export_raw_prices to see real-world code snippets that use this hook.

wp_all_export_repeater_delimiter

DescriptionDefine the delimiter used for ACF Repeater fields. Default is comma.

Parameters

ParamTypeDescription$implode_delimiterstringThe delimiter to use. Default ",".$export_idintThe export ID.

Usage

add_filter( 'wp_all_export_repeater_delimiter', 'wpae_wp_all_export_repeater_delimiter', 10, 2 );

function wpae_wp_all_export_repeater_delimiter( $implode_delimiter, $export_id ) {
return $implode_delimiter;
}

Search the docs for wp_all_export_repeater_delimiter to see real-world code snippets that use this hook.

wp_all_export_use_csv_compliant_line_endings

DescriptionUse custom CSV writer when affected by https://bugs.php.net/bug.php?id=43225.

Parameters

ParamTypeDescription$use_compliant_endingsboolUse compliant endings? true / false.

Usage

add_filter( 'wp_all_export_csv_strategy', function( $csvStrategy ) {
return WpaeCsvCsvWriter::CSV_STRATEGY_CUSTOM;
}, 10, 1 );
add_filter( 'wp_all_export_use_csv_compliant_line_endings', function( $useCsvCompliantLineEndings ) {
return true;
}, 10, 1 );

Search the docs for wp_all_export_use_csv_compliant_line_endings to see real-world code snippets that use this hook.

wp_all_export_xml_rows

DescriptionFilter a single XML record for conditional export. See wp_all_export_csv_rows for CSV exports.

Parameters

ParamTypeDescription$is_export_recordboolInclude record in export? true / false.$recordobjectRecord object.$export_optionsarrayThe export options.$export_idintThe export ID.

Usage

function my_wp_all_export_xml_rows( $is_export_record, $record, $export_options, $export_id ) {
// Unless you want this code to execute for every export, be sure to check the export id
//
// if ( $export_id == 5 ) { ...
// Check $record object and return true to export or false to skip
return true;
}
add_filter( 'wp_all_export_xml_rows', 'my_wp_all_export_xml_rows', 10, 4 );

Search the docs for wp_all_export_xml_rows to see real-world code snippets that use this hook.

wp_all_export_zapier_response

DescriptionCan be used to manipulate the response sent to Zapier.

Parameters

ParamTypeDescription$responsearrayThe response to Zapier.

Usage

add_filter( 'wp_all_export_zapier_response', 'wpae_wp_all_export_zapier_response', 10, 1 );

function wpae_wp_all_export_zapier_response( $response ) {
// Code here.
return $response;
}

Search the docs for wp_all_export_zapier_response to see real-world code snippets that use this example.

pmxi_custom_types

This filter allows modifying the option shown in the post type dropdown on Step 1 of an import. The current list of types and the list being updated are available as parameters. The custom_types list is where you'd add a post type to the dropdown that doesn't otherwise appear. The hidden_post_types list allows removing items from the dropdown.

Parameters

ParamTypeDescription$custom_typesarrayThe list of post types that will be available.$typestringWhich type of list is being updated - custom_types or hidden_post_types

Usage

function wpai_custom_types( $custom_types ) {

// Modify the custom types to be shown on Step 1.

// Return the updated list.
return $custom_types;

}
add_filter( 'pmxi_custom_types', 'wpai_custom_types', 10, 1 );

Search the docs for pmxi_custom_types to see real-world code snippets that use this filter.

pmxi_visible_template_sections

This filter allows modifying what sections are shown on Step 3 and Manage Imports > Edit Import. It provides an array of sections set to show and the post type being imported.

Parameters

ParamTypeDescription$sectionsstring arrayThe sections to show on Step 3. Possible options:'caption', 'main', 'taxonomies', 'cf', 'featured', 'other', 'nested'$post_typestringThe post type being imported.

Usage

function show_addon_section_users_customers( $sections, $post_type ) {

// Do something for your post type.
if ( 'your_post_type' == $post_type )

// Do something.

return $sections;
}

add_filter( 'pmxi_visible_template_sections', 'show_addon_section_users_customers', 11, 2 );

Search the docs for pmxi_visible_template_sections to see real-world code snippets that use this filter.

wp_all_import_minimum_number_of_variations

DescriptionUse this hook to set the minimum number of variations a product can have before it's converted to a simple product by the "Create products with no variations as simple products" option.

Parameters

ParamTypeDescription$min_variationsintMinimum number of variations. Default 2.$product_idboolThe Product ID.$import_idstringThe Import ID.

Usage

add_filter( 'wp_all_import_minimum_number_of_variations', 'wpai_wp_all_import_minimum_number_of_variations', 10, 3 );function wpai_wp_all_import_minimum_number_of_variations( $min_number_of_variations, $pid, $import_id ) {    return 1;}

Search the docs for wp_all_import_minimum_number_of_variations to see real-world code snippets that use this hook.

wpai_is_case_insensitive_taxonomy_mapping

DescriptionReturn true to make the mapping rules in the "Enable Mapping for Categories" section case insensitive. Added in WP All Import v4.7.0-beta-1.1.

Parameters

ParamTypeDescription$is_case_insensitiveboolDefaults to false.$tx_namestringThe taxonomy name.

Usage

add_filter( 'wpai_is_case_insensitive_taxonomy_mapping', 'my_is_case_insensitive_mapping', 10, 2 );

function my_is_case_insensitive_mapping( $is_case_insensitive, $tx_name ) {
// Return true to make it case insensitive
return $is_case_insensitive;
}

Search the docs for wpai_is_case_insensitive_taxonomy_mapping to see real-world code snippets that use this hook.

wp_all_import_post_skipped

DescriptionFires whenever an import record is SKIPPED.

Parameters

ParamTypeDescription$post_to_update_idintThe post ID, or 0 if there's no post.$import_idintThe import ID.$current_xml_nodearrayAn array with the current import record's data.

Usage

// Log all skips
add_action( 'wp_all_import_post_skipped', 'my_log_skips', 10, 3 );

function my_log_skips( $created, $import_id, $xml_data ) {
$uploads = wp_upload_dir();
$log_file_name = $uploads['basedir'] . '/' . $import_id . '_import_custom_log.txt';
if ( file_exists( $log_file_name ) ) {
$existing_data = file_get_contents( $log_file_name );
} else {
touch( $log_file_name );
$existing_data = '';
}

if ( $created == 0 ) {
$created = 'N/A';
}

if ( array_key_exists( 'sku', $xml_data ) && array_key_existS( 'title', $xml_data ) ) {
$existing_data .= '[ID: ' . $created . '] [SKU: ' . $xml_data['sku'] . '] [Title: ' . $xml_data['title'] . '] has been skipped.' . PHP_EOL;
file_put_contents( $log_file_name, $existing_data );
}
}

Search the docs for wp_all_import_post_skipped to see other real-world code snippets that use this hook.

Adding Support For Your Theme or Plugin

Adding Support For Your Theme or Plugin

WP All Import can be extended using our Add-On API to fully support any plugin or theme. Does your plugin or theme use custom database tables, inter-related post types, or some other unique setup? No problem, as you can write custom code to accommodate any situation.

Are you a plugin or theme developer?

Creating a WP All Import add-on will give your users an easy and free way to import their data to your plugin or theme.

An add-on can make things MUCH easier for your users if your plugin/theme doesn』t store data in the 「normal」 places in WordPress.

For example, many plugins and themes use nested serialized arrays and custom gallery fields to store data, but WP All Import doesn』t provide a simple way to import to those.

Note: Add-ons can be used with both the free and paid versions of WP All Import. This means all of your users will have an easy way to make use of the add-on you create.

Best of all, coding add-ons is extremely easy using our Rapid Add-On API.

An add-on is just a normal WordPress plugin, and a simple add-on can be done with less than 20 lines of code.

Continue to Add-On Structure to learn how to code an add-on.

What's Next?

Add-On Structure

Best Practices

A Complete Add-On

Related

Text Fields

Radio Fields

Image Fields

Nested Fields