Detecting and Syncing Additional Fields

Detecting and Syncing Additional Fields

#Overview
If you』re reading this page then you』re probably trying to sync a field value with your CRM that isn』t currently supported by WP Fusion.
While WP Fusion can sync any data from WordPress to contact records in your CRM, it does not work 「auto-magically」 with every piece of data you see in your WordPress admin. That has to do with where your field data is stored.
We can break the kinds of data that WP Fusion syncs into three categories:

Data from the normal user tables – Works great with no extra setup
User data that』s in a different table – Requires some work
Data that』s calculated or not related to a specific user – Can be difficult

#Normal tables
WP Fusion can automatically detect and sync any data found in the wp_users and wp_usermeta database tables bidirectionally with any fields in your CRM.
Syncing data between just two tables (your users and your CRM) is automatic and requires no extra setup
These tables are the standard place where most plugins store data relating to a specific user. So even if WP Fusion doesn』t have a specific integration with a plugin, it』s likely the fields from that plugin will still show up under the Additional Fields section in the Contact Fields settings, and can be synced with your CRM without any extra work.
Note: To build the list of Additional Fields, for performance reasons WP Fusion only scans the usermeta of your own admin user. If you』ve just added a new field and it』s not showing, try saving some data in that field for your own admin user.
#User data that』s in a different table
Some plugins, like BuddyPress, store user profile data in different database tables.
When we talk about WP Fusion having an 「integration」 with a membership plugin (like the ones listed here), that means WP Fusion has additional code to:

Detect the available fields from that plugin』s custom tables, and list them in the Contact Fields in the settings
Extract the data from the custom tables and sync it to your CRM
Detect when data has been loaded from your CRM, and if that data belongs in a custom table, store it there

For example with BuddyPress, let』s say we have a custom XProfile field for Favorite Color, in the wp_bp_xprofile_data table. How that sync looks visually is something like:
Syncing data between your CRM and multiple WordPress tables can require some extra attention if using a non-supported plugin
And then in the code, that requires three functions:
#Detect the custom fields and list them for sync
Step one is figuring out which fields are available, and using the wpf_meta_fields filter to register them as available for sync.
With BuddyPress we make use of the bp_xprofile_get_groups() function, like
function example_prepare_buddypress_fields( $meta_fields ) {

// Get the field groups

$groups = bp_xprofile_get_groups( array(
'fetch_fields' => true,
) );

foreach ( $groups as $group ) {

foreach ( $group->fields as $field ) {

// Register the field in the list using the ID and label

$key = 'bbp_field_' . $field->id;

$meta_fields[ $key ] = array(
'label' => $field->name,
'type' => $field->type,
);

}
}

return $meta_fields;

}

add_filter( 'wpf_meta_fields', 'example_prepare_buddypress_fields' );
Which makes the fields show up for sync in the settings like this:

#Extract the data from the custom table and sync it to the CRM
When a user registers or a Push User Meta operation is run, WP Fusion tries to get all of the data it can out of the database for that user.
That data is then passed through the wpf_get_user_meta filter, which allows us to merge in data from different sources.
For example the code to load and sync the XProfile data would look like:
function example_get_buddypress_fields( $user_meta, $user_id ) {

$profile_data = BP_XProfile_ProfileData::get_all_for_user( $user_id );

// Get the profile data and merge it into the user_meta

foreach ( $profile_data as $field ) {
$key = 'bbp_field_' . $field['field_id'];
$user_meta[ $key ] = $field['field_data'];
}

return $user_meta;

}

add_filter( 'wpf_get_user_meta', 'example_get_buddypress_fields', 10, 2 );
#Load data from the CRM into the custom tables
Then, going the other direction, we want to make sure that data loaded from the CRM is properly stored in the custom database table, not the wp_usermeta table.
For that we make use of the wpf_set_user_meta filter. For example with BuddyPress, that code looks like:
function example_set_buddypress_fields( $user_meta, $user_id ) {

foreach ( $user_meta as $key => $value ) {

if ( strpos( $key, 'bbp_field_' ) === 0 ) { // If the field key starts with bbp_field_

$field_id = str_replace( 'bbp_field_', '', $key );

$field = new BP_XProfile_ProfileData( $field_id, $user_id );
$field->value = $value;
$field->save(); // Save the field to the wp_bp_xprofile_data table

unset( $user_meta[ $key ] ); // unset() the value so it's not saved to wp_usermeta

}
}

return $user_meta;

}

add_filter( 'wpf_set_user_meta', 'example_set_buddypress_fields', 10, 2 );
And there you have it. With those three code snippets, you can register and bidirectionally sync a user meta field from a custom database table.
#Data that』s not in a table
There may be some things you』d like to sync with your CRM that aren』t actually stored in any one place. Some examples:

Customer lifetime value
Most recently commented post
Most recent quiz score
Subscription renewal date

With things like this, they aren』t actually stored in any one place in the database that can be directly synced with your CRM, you』d have to write the code to calculate the values.
#Register the field
For example, let』s say you want to sync a WooCommerce customer』s lifetime value with your CRM. First register the field in the WP Fusion settings.
function example_prepare_ltv_field( $meta_fields ) {

$meta_fields['lifetime_value'] = array(
'label' => 'Lifetime Value',
'group' => 'woocommerce',
'pseudo' => true,
);

return $meta_fields;

}

add_filter( 'wpf_meta_fields', 'example_prepare_ltv_field' );
In this example we』ve set pseudo to true to indicate that the field doesn』t really exist. This is optional but it stops WP Fusion from loading the value back from your CRM, and filling up your database with unnecessary meta values.
#Calculate and sync the data
Similar to the BuddyPress example, we』ll use the wpf_get_user_meta filter to merge the custom value into the data that』s being sent to the CRM.
function example_sync_lifetime_value( $user_meta, $user_id ) {

$user_meta['lifetime_value'] = 0;

$customer_orders = get_posts( array(
'posts_per_page' => -1,
'post_type' => 'shop_order',
'post_status' => wc_get_is_paid_statuses(),
'meta_key' => '_billing_email',
'meta_value' => $user_meta['billing_email'],
'orderby' => 'ID',
'order' => 'DESC',
));

if ( ! empty( $customer_orders ) ) {

foreach ( $customer_orders as $order_id ) {

$order = wc_get_order( $order_id );

$order_total = $order->get_total();

$user_meta['lifetime_value'] += floatval( $order_total );

}
}

return $user_meta;

}

add_filter( 'wpf_get_user_meta', 'example_sync_lifetime_value', 10, 2 );
In this case, the code is quite a bit more complicated, since we』re not just pulling the data out from another part of the database. To calculate the lifetime value you first need to search for all orders placed by that customer, and then for each order increment the lifetime value field by that order』s total.
This also can introduce performance problems on stores with a large number of orders. In those cases you might want to cache the value using a transient, or create a separate meta key that tracks the customer』s LTV and is incremented with each new order.
#Examples
#Sync booking dates from multiple FooEvents products to different date fields in the CRM

#Sync subscription fields from multiple subscription products to their own fields
This example works with WooCommerce subscriptions, it registers each of your subscription products as a separate field on the contact fields list. For each you can sync the renewal date, status, and start date to a separate custom field in your CRM.

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

How To Submit A Job On Codeable For Custom Development

How To Submit A Job On Codeable For Custom Development

#Overview
Looking to expand the functionality of WP Fusion?
We』ve partnered with a team of skilled experts through Codeable to help you with your premium WordPress and WP Fusion development needs.
#What is Codeable?
Codeable is a trusted 3rd-party provider of vetted WordPress developers who can help you with any of your WordPress development needs. To ensure you』re matched with the right developer, they have a thorough application process to make sure you get the results you want.
#How to submit a job on Codeable for custom development
1. Go to our custom Codeable link and click the Get a Free Estimate button.

2. On the next page, you』ll be given two options. You can either Start a Project or Start a Consultation and speak directly with Codeable experts before starting your project.
For the purposes of this doc, we』ll be starting a new project. Click the Start a Project button to proceed.

3. The next page will allow you to enter the information for your project. First, select the type of project you need help with from the I need help with: dropdown.
The next page will allow you to enter the information for your project. First, select the type of project you need help with from the I need help with: dropdown.

Next, in the My: section, select Plugin from the dropdown if you need help customizing WP Fusion. If not, select the help you need.
Once ready, click the Continue button.

4. On the next page, you』ll be able to add details to your project including:

The project title.
The project description.
A URL.
Files and screenshots for extra information.
And you』ll be able to indicate if you want a specific expert to work on your project, or not.


5. Once happy, click Continue and move onto the next step.
6. On the next page, you』ll be able to indicate how complex and urgent your project is. Make your selection and you』ll see a rough estimate from Codeable. If you』re happy, click Continue.
 
7. Next, if you don』t already have one, you』ll be asked to create a Codeable account so that you can see the progress of your project and talk to developers.
8. Once ready, click Publish my Project and Codeable will get to work matching you with the right developers for the job.
#What happens next?
Once your project is submitted, you』ll be left in the capable hands of Codeable. Here』s some information from them to help:

Good luck with your project, we hope you get the results you』re looking for!

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

How to use a custom client ID for authentication

How to use a custom client ID for authentication

#Overview
With some CRMs, WP Fusion is registered as an app via those platforms』 developer programs. Because of this, you need an account on our site and a WP Fusion license to complete the initial authentication process with your CRM.
This applies to:

BirdSend
Drift
HubSpot
NationBuilder
Salesforce
Zoho

There may be scenarios where you want to use your own client ID for authentication, and not the WP Fusion client ID. For example if you have a created a custom branded app via your CRM』s developer program, or if you don』t have a WP Fusion license.
#Using a custom client ID and authorization URL
The client ID and client secret are public properties on each CRM』s integration class. You can override them by hooking into the wp_fusion_init_crm action, which passes the CRM object by reference.
For example with Zoho:
function set_custom_zoho_app( &$crm ) {

$crm->client_id = '1000.XXXXXXXXXXXXXXXXXXXXX';
$crm->client_secret_us = '08dccXXXXXXXXXXXXXXXXXXXXX';

}

add_action( 'wp_fusion_init_crm', 'set_custom_zoho_app' );
To override the initial authorization request URI for the 「Authorize」 button in the WP Fusion settings, use the wpf_{$crm}_auth_url filter, for example with Zoho:
function set_custom_zoho_auth_url() {

return "https://accounts.zoho.com/oauth/v2/auth?scope=ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.users.READ,ZohoCRM.org.READ &response_type=code&access_type=offline&redirect_uri=https%3A%2F%2Fmysite.com%2Fzoho.php&prompt=consent&client_id=XXXX";

}

add_action( 'wpf_zoho_auth_url', 'set_custom_zoho_auth_url' );
Note that in this example you must have registered https://mysite.com/zoho.php as the redirect URI for your app, and must have a script running at that location which is capable of listening for the authorization response and redirecting back to the WP Fusion settings page with the &code= parameter in the URL.
#Salesforce example
To set your own custom Salesforce client ID and secret:
function set_custom_salesforce_app( &$crm ) {

$crm->client_id = '3MVG9CEn_O3jvv0xMf5rhesocm_5czidz9CFtu_qNZ2V0Zw.bmL0LTRRylD5fhkAKYwGxRDDRXXXXXXXXXXXX';
$crm->client_secret = '9BB0BD5237B1EA6ED8AFE2618053XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

}

add_action( 'wp_fusion_init_crm', 'set_custom_salesforce_app' );
To override the initial OAuth authorization URL:
function set_custom_salesforce_auth_url() {

$args = array(
'client_id' => wp_fusion()->crm->client_id,
'redirect_uri' => rawurlencode( admin_url( 'options-general.php?page=wpf-settings&crm=salesforce' ) ),
'response_type' => 'code',
'scope' => 'api%20refresh_token%20offline_access',
);

$url = add_query_arg( $args, 'https://login.salesforce.com/services/oauth2/token' );

return $url;

}

add_action( 'wpf_salesforce_auth_url', 'set_custom_salesforce_auth_url' );

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

Registering Custom Batch Operations

Registering Custom Batch Operations

#Overview
WP Fusion includes several batch tools that allow you to perform operations in bulk, such as importing users, exporting user meta data, and syncing historical WooCommerce orders to your CRM.
The batch system is based on WP Background Processing, and it can be modified or extended via the use of filters.
#How it works
The batch system makes use of three filters:

wpf_export_options: Registers the operation title and slug, and adds it to the list of available batch tools on the Advanced settings tab
wpf_batch_{slug}_init: Queries the objects IDs (user IDs, post IDs, etc) to be processed and returns them as an array
wpf_batch_{slug}: Is called on each record in the queue

The available batch operations will vary depending on which plugins are active. It』s also possible to register your own batch operations.
#Examples
#Limit the WooCommerce orders export by date
By default the WooCommerce orders export will query all orders that have yet to be processed by WP Fusion (indicated by the wpf_complete postmeta value).
This filter runs on the same operation slug (woocommerce), but at priority 20. This overrides WP Fusion』s built in query, and does a new query only on orders placed since March 1st 2020.
function my_custom_export( $order_ids ) {

$args = array(
'numberposts' => - 1,
'post_type' => 'shop_order',
'post_status' => array( 'wc-processing', 'wc-completed' ),
'fields' => 'ids',
'order' => 'ASC',
'meta_query' => array(
array(
'key' => 'wpf_complete',
'compare' => 'NOT EXISTS',
),
),
'date_query' => array(
'after' => 'March 1 2020',
),
);

$order_ids = get_posts( $args );

wpf_log( 'info', 0, 'Beginning WooCommerce Orders batch operation on ' . count( $order_ids ) . ' orders', array( 'source' => 'batch-process' ) );

return $order_ids;

}

add_filter( 'wpf_batch_woocommerce_init', 'my_custom_export', 20 );
#Export Easy Digital Downloads order date by month
Syncs the order date and time to the connected CRM for all non-recurring EDD payments made in November of the current year.
/**
* Register the export option.
*
* @return array Options
*/
function edd_export_options( $options ) {

$options['edd_orderdate'] = array(
'label' => __( 'EDD Order Date', 'wp-fusion' ),
'title' => __( 'Orders', 'wp-fusion' ), //
'tooltip' => __( 'Custom: Sync order dates for non-recurring orders in the month of November.', 'wp-fusion' ),
);

return $options;

}

add_filter( 'wpf_export_options', 'edd_export_options' );

/**
* Query the payment IDs to be processed.
*
* @return array Payment IDs
*/

function edd_batch_init() {

$args = array(
'number' => -1,
'fields' => 'ids',
'monthnum' => 11,
'year' => date( 'Y' ),
'post_status' => 'publish',
);

$payments = edd_get_payments( $args );

return $payments;

}

add_filter( 'wpf_batch_edd_orderdate_init', 'edd_batch_init' );

/**
* Sync the order date for each payment
*/

function edd_batch_step( $payment_id ) {

$payment = new EDD_Payment( $payment_id );

$update_data = array(
'order_date' => $payment->get_meta( '_edd_completed_date' ),
);

wp_fusion()->user->push_user_meta( $payment->user_id, $update_data );

}

add_action( 'wpf_batch_edd_orderdate', 'edd_batch_step' );
#Pull user meta for users who registered before a specific date
This example limits the Pull User Meta operation just to users who registered after January 1st 2019.
function limit_by_user_registered( $user_ids ) {

// At this point $user_ids is all users with a CRM contact ID

foreach ( $user_ids as $i => $user_id ) {

$user = get_userdata( $user_id );

if ( strtotime( $user->user_registered ) 'Resync Tags (No Contact ID)',
'title' => 'Users',
'tooltip' => 'Resyncs the contact ID and tags just for users that don't have a stored contact ID.',
);

return $options;

}

add_filter( 'wpf_export_options', 'no_cid_export_options' );

/**
* No contact ID batch init
*
* @return array Users
*/

function no_cid_init() {

$args = array(
'fields' => 'ID',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => wp_fusion()->crm->slug . '_contact_id',
'compare' => 'NOT EXISTS',
),
array(
'key' => wp_fusion()->crm->slug . '_contact_id',
'value' => false,
),
),
);

$users = get_users( $args );

return $users;

}

add_action( 'wpf_batch_no_cid_init', 'no_cid_init' );

/**
* No contact ID batch - single step
*
* @return void
*/

function no_cid_step( $user_id ) {

wp_fusion()->user->get_tags( $user_id, true );

}

add_action( 'wpf_batch_no_cid', 'no_cid_step' );

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

Connecting to two different ActiveCampaign accounts

Connecting to two different ActiveCampaign accounts

#Overview
This is an case study in how we』ve use the classes and methods discussed earlier in this section to help a customer solve a very unique problem. This customer is using ActiveCampaign as his CRM, and has a very large contact list (over 25,000 contacts).
This customer had two types of WordPress users he needed to track in ActiveCampaign: Customers, and Affiliates. For his small group of affiliates, he needed the pipeline automation and sales attribution features that come with ActiveCampaign』s Professional plan. But a Professional plan for 25,000 contacts would cost $599 a month.
#The approach
It didn』t make sense to be paying for the Professional package when those features were only needed for a small subset of his contacts. So we proposed to set up two ActiveCampaign accounts— one on the Lite plan for his primary list, and a second Professional account for his affiliates and partners.
#The solution
Out of the box, WP Fusion can only connect to one CRM at a time. But the flexibility of WP Fusion』s framework makes it easy to solve even very complex requirements like this. You can see the finished plugin file on GitHub here, or continue reading below for a more detailed explanation.
#Setting things up
As we discussed in the introduction to WP Fusion』s CRM API, all communication with your CRM is done via interfacing with the wp_fusion()->crm object. In this case, it was configured to connect to the main ActiveCampaign account, the one with all of the customers.
Let』s call that Account A — Customers
What we want to do is create a new wp_fusion()->crm object, this time connected to the AC account for affiliates and partners.
We』ll call that Account B — Affiliates
When an affiliate registers or updates their account, we』ll temporarily swap the CRM objects, so the contact data is sent to the right place.
define( 'ACCOUNT_TWO_URL', 'https://account2.api-us1.com' );
define( 'ACCOUNT_TWO_KEY', 'APIKEY' );

define( 'WPF_DISABLE_QUEUE', true );

global $wpf_ac_app_swap;
global $wpf_ac_switched;

$wpf_ac_app_swap = false;
$wpf_ac_switched = false;

In the first lines of the plugin, we』re defining two constants, ACCOUNT_TWO_URL, and ACCOUNT_TWO_KEY. This will be used to initialize the connection to the second account.
We also need to disable WP Fusion』s API queue in this case, because it doesn』t play nicely with swapping the active CRM multiple times on a page load.
Finally, we set two globals:

$wpf_ac_app_swap: When we swap the wp_fusion()->crm object from Account A to Account B, this global will hold onto Account A until we』re ready to switch back.
$wpf_ac_app_switched: This will provide an easy way to check whether we』re connected to Account A or Account B. When we switch to Account B, $wpf_ac_switched is set to 「true」. And when we switch back, it』s returned to 「false」

#Determining when to switch
function wpf_is_account_two( $user_id ) {

$roles = array( 'deals_contributor' );
$user = get_userdata( $user_id );

if ( affwp_is_affiliate( $user_id ) ) {
return true;
}

foreach ( $roles as $role ) {
if ( 'deals_contributor' === $user->role ) {
return true;
}
}

return false;
}

In order to know when the accounts need to be switched, we have this helper function at the top of the plugin. It accepts a user ID, and if the user has a role 「deals_contributor」, or if they』re a registered AffiliateWP affiliate, then the user has to go to Account B.
If the user doesn』t meet those criteria, the function returns false, and the data is sent to Account A.
#Switching from Account A to Account B
We』ve included add_action()』s for every relevant function in the WPF_User class, and set wpf_maybe_switch_to_account_two() as the callback. These actions are triggered at the start of each function, before any data has been sent. We』ll use this to determine if the account needs to be switched.

function wpf_maybe_switch_to_account_two( $user_id ) {

global $wpf_ac_app_swap;
global $wpf_ac_switched;

if( wpf_is_account_two( $user_id ) && $wpf_ac_switched == false ) {

// If user should be sent to second app, and the first app is currently active
if( $wpf_ac_app_swap == false ) {

// If apps haven't been swapped yet, move first app into swap variable
$wpf_ac_app_swap = wp_fusion()->crm->app;

// And initialize second app connection
wp_fusion()->crm->connect( ACCOUNT_TWO_URL, ACCOUNT_TWO_KEY, true );

} else {

// If second app is already saved in the swap, move it to a temp var
$temp_second_app = $wpf_ac_app_swap;

// Store first app in swap
$wpf_ac_app_swap = wp_fusion()->crm->app;

// Put second app back into use
wp_fusion()->crm->app = $temp_second_app;

}

// Set $wpf_ac_switched to true to indicate we're using the second app
$wpf_ac_switched = true;

}
}

add_action( 'wpf_user_register_start', 'wpf_maybe_switch_to_account_two' );
add_action( 'wpf_get_contact_id_start', 'wpf_maybe_switch_to_account_two' );
add_action( 'wpf_pre_pull_user_meta', 'wpf_maybe_switch_to_account_two' );
add_action( 'wpf_get_tags_start', 'wpf_maybe_switch_to_account_two' );
add_action( 'wpf_apply_tags_start', 'wpf_maybe_switch_to_account_two' );
add_action( 'wpf_remove_tags_start', 'wpf_maybe_switch_to_account_two' );
add_action( 'wpf_push_user_meta_start', 'wpf_maybe_switch_to_account_two' );

This function works as follows:

We first load up the globals for $wpf_ac_app_swap and $wpf_ac_switched so the function is aware of the current state of things.
The function then checks to see if the user in question should be sent to Account B, by calling our wpf_is_account_two() function.
It then confirms that $wpf_ac_switched is false. Because if we』ve already switched to Account B somewhere else, we don』t want to do it again.
If the user passes those checks, we check the $wpf_ac_app_swap global to see if it』s already been set up and connected to Account B, perhaps because we』ve already swapped the accounts earlier in the registration process.

If there』s nothing in the $wpf_ac_app_swap variable, that means we need to set up the new wp_fusion()->crm object and configure it with the Account B API credentials.
But first, we move the currently active Account A CRM object into the $wpf_ac_app_swap variable, so it can easily be put back later.
Then we call up wp_fusion()->crm->connect() with the Account B connection credentials.
After this point, any data sent to the CRM object will now be directed to Account B.

On the other hand, if $wpf_ac_app_swap isn』t empty, then we don』t need to initialize the connection all over again. The Account B CRM object is already there waiting for us.

We first move the Account B CRM object into a temporary variable so it』s not overwritten.
Then we set the $wpf_ac_app_swap to contain the CRM object for Account A, so we can put it back it again later.
And finally we set the Account B CRM object we』d stored in the temporary variable.

And as the very last step, we set $wpf_ac_switched to 「true」 to indicate that WP Fusion is now connected to Account B.

#Switching from Account B back to Account A
Switching the accounts back follows the same basic logic in the function above, except we』re now using action hooks that fire at the end of each function, after the data has been sent. The code looks like this:
function wpf_maybe_switch_back( $user_id ) {

global $wpf_ac_app_swap;
global $wpf_ac_switched;

if( $wpf_ac_switched == true ) {

// If the second app is active, move the first app from the swap to a temp variable
$temp_first_app = $wpf_ac_app_swap;

// Store second app in swap
$wpf_ac_app_swap = wp_fusion()->crm->app;

// Put first app back into use
wp_fusion()->crm->app = $temp_first_app;

// Set $wpf_ac_switched to false
$wpf_ac_switched = false;

}
}

add_action( 'wpf_user_created', 'wpf_maybe_switch_back' );
add_action( 'wpf_got_contact_id', 'wpf_maybe_switch_back' );
add_action( 'wpf_user_updated', 'wpf_maybe_switch_back' );
add_action( 'wpf_tags_modified', 'wpf_maybe_switch_back' );

This should be pretty clear to follow if you』ve read the more detailed walkthrough above. It』s basically the same process as switching from Account A to Account B, but in reverse.
Though the Account B CRM object is no longer needed at this point, we still store it in the $wpf_ac_app_swap variable, so it can easily be be accessed in a future process, without having to set up the connection again from scratch.
#In summary
Obviously this is a very niche problem that we』ve solved, and likely only applicable to this one customer.
But it hopefully demonstrates how WP Fusion, as a framework, can be extended to accommodate even the most complex business requirements.
If you view the Gist on GitHub, you』ll see we did all of this in just 56 lines of code.
 
We really enjoy coming up with these kinds of solutions for our customers. If you』re interested in discussing implementation ideas for your own project, send us a message, we』d love to hear from you!
 

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

Scheduled Synchronization Using Cron

Scheduled Synchronization Using Cron

#Preface
If you』re reading this page and you』re new to WP Fusion, then you』ve come to the wrong place.
WP Fusion can automatically sync data bidirectionally with your CRM without the use of cron jobs.
That』s achieved by setting up webhooks in your CRM, so your CRM notifies WP Fusion when a record has been modified, and the updated data is loaded into WordPress at that time.
Webhooks are way more efficient than loading data using a cron job or scheduled action.
#Overview
There may be some scenarios where you need to re-sync or import data on a schedule, outside of webhooks.
For example maybe your CRM doesn』t support webhooks for the trigger you need, or your site is hosted on an internal network and can』t receive data from outside systems.
In that case you can schedule any one of WP Fusion』s batch operations using WordPress』 cron system.
To do so, you make a call to wp_fusion()->batch->batch_init( $method ); in your scheduled event callback, where $method is the name of the operation you』d like to perform.
You can see the internal batch operation names by inspecting the HTML radio values for the various batch operations on the Advanced tab in the WP Fusion settings.
The operation name will be the value parameter on the element.
#Examples
#Import every contact with a specific tag, daily
This snippet imports every new contact with tag ID 123, once daily.
function do_wpf_daily_import() {

$args = array(
'tag' => 123, // The tag ID or list ID (with HubSpot) to import
'role' => 'subscriber',
'notify' => false, // Set to true to send a welcome email
);

wp_fusion()->batch->batch_init( 'import_users', $args );

}

add_action( 'wpf_daily_import', 'do_wpf_daily_import' );

if ( ! wp_next_scheduled( 'wpf_daily_import' ) ) {
wp_schedule_event( time(), 'daily', 'wpf_daily_import' );
}
#Resync tags for all users every Friday
This snippet runs a Resync Tags operation every Friday.
function do_wpf_update_tags() {

if ( date ('l') !== 'Friday' ) {
return;
}

wp_fusion()->batch->batch_init( 'users_tags_sync' );

}

add_action( 'wpf_update_tags_weekly', 'do_wpf_update_tags' );

if ( ! wp_next_scheduled( 'wpf_update_tags_weekly' ) ) {
wp_schedule_event( time(), 'daily', 'wpf_update_tags_weekly' );
}
#Load metadata for all users, daily
This snippet runs a Pull User Meta operation daily at 5pm UTC.
function do_wpf_pull_meta() {

wp_fusion()->batch->batch_init( 'pull_users_meta' );

}

add_action( 'wpf_pull_meta_daily', 'do_wpf_pull_meta' );

if ( ! wp_next_scheduled( 'wpf_update_tags_daily' ) ) {
wp_schedule_event( strtotime( '5pm' ), 'daily', 'wpf_pull_meta_daily' );
}
#Sync WooCommerce orders daily
This operation runs daily at mightnight and syncs any WooCommerce orders to your CRM that haven』t yet been synced.
function do_wpf_orders_sync() {
wp_fusion()->batch->batch_init( 'woocommerce' );
}

add_action( 'wpf_daily_order_sync', 'do_wpf_orders_sync' );

if ( ! wp_next_scheduled( 'wpf_daily_order_sync' ) ) {
wp_schedule_event( strtotime( '12am' ), 'daily', 'wpf_daily_order_sync' );
}
#Tips
#Monitoring
We』d recommend using a cron management plugin like Advanced Cron Manager or WP Crontrol to ensure that the event has been scheduled, and to test the task.
#Performance
The background processes works through records sequentially (there are no parallel threads), and is designed to use no more than 80% of your site』s available memory. Generally speaking, the background process should not be able to take your site offline.
However, it can make your site noticeably slower.
As a rule of thumb, assume that every user / record to be synced requires 5 seconds of processing time.
That means if you』re running a Resync Tags operation on 1,000 users daily, your site will be slowed down for about 80 minutes every day. That』s not the end of the world if you schedule the operation during the middle of the night.
However, if you have 10,000 users, the operation would be running for 13 hours of every day… which would likely cause a noticeable performance decrease. In that case it would be preferable to set the cron schedule so the task only runs once a week instead of once a day.

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

Contributing Integration Modules

Contributing Integration Modules

#Overview
WP Fusion includes integration modules with about 100 WordPress plugins. Each module is contained in a single class.
It』s easy to create a new WP Fusion integration for your own plugin, and by doing so enable your plugin to communicate bidirectionally with over 40 CRMs and marketing automation tools.
For a bootstrap to get you started, download this example plugin from GitHub.
#Setting up a custom integration module
First download the starter plugin. Then do a find and replace (case sensitive) on four strings:

「my-plugin-slug」: This is the slug used to identify your integration.
「My_Plugin_Name」: This is the class name for the integration.
「My Plugin Name」: This is the human-readable name for the plugin integration
「My/PluginDependencyClass」: This is a class name in your plugin. WP Fusion will do a class_exists()on this string when determining whether or not to load the integration module.

Also make sure to rename /includes/class-my-plugin-slug.php to reflect the new slug.
#Suggested functionality
The hooks used and functionality in your integration module will depend on the type of plugin, but generally:
#Ecommerce integrations
WP Fusion』s ecommerce integrations generally:

Sync customers to the CRM at checkout, including their name, email address, billing details, and any custom fields
Sync guest checkouts to the CRM and attach the guest』s contact ID to the order meta
Allow the user to configure tags to be applied per product at checkout

For examples in WP Fusion see classes WPF_EDD, WPF_Simple_Pay.
#Membership integrations
WP Fusion』s membership integrations:

Detect custom fields that have been created for user registration or profiles, and make them available for sync using the wpf_meta_fields filter (example provided in the sample plugin)
Detect a user registration and make sure that any POST』ed custom field values are properly merged into the output from the wpf_user_register filter (example provided in the sample plugin)
Detect a profile update and likewise make sure that any POST』ed custom fields are synced to the CRM with the rest of the data
(If applicable) Apply tags in the CRM based on membership level and membership status. For example with MemberPress or WooCommerce Memberships.

For examples in WP Fusion see classes WPF_User_Meta, WPF_Simple_Membership, WPF_Clean_Login.
#Form integrations
WP Fusion』s form integrations:

Register a field mapping interface within the form』s settings or form edit screens that allow mapping form fields with CRM fields (see for example the field mapping interfaces in Ninja Forms, Gravity Forms, or Formidable Forms.
Include a setting in the field mapping interface for Apply Tags, so the user can specify CRM tags to be applied when the form is submitted
Detect when a form is submitted and extract the submitted values from the form, passing them to WPF_Forms_Helper::process_form_data() (see code examples in integrations mentioned above), and then save the new CRM contact ID to the form entry meta

#Event integrations
WP Fusion』s event and booking plugin integrations:

Detect custom fields that have been created for event registration or RSVP forms, and make them available for sync using the wpf_meta_fields filter (example provided in the sample plugin)

Preferably allows syncing 「pseudo」 event fields such as event date and time, or venue, for example see FooEvents

Detect an event registration and make sure that any attendee fields and custom event fields are properly synced to the CRM (including from guest registrations)
Add a meta box or setting input to the event or ticket editor that allows tags to be configured to be applied for event registration, and apply those tags during event registration

For examples in WP Fusion see classes WPF_FooEvents, WPF_Events_Manager, WPF_Modern_Events_Calendar.
#Contributing to WP Fusion
We welcome and encourage new submissions for custom integration modules.
To do that first make a fork of the integration boostrap plugin. Once your integration is finished, drop us a line with a link to your fork, we』ll review the integration, and (with your permission) include it in future updates of WP Fusion.
#Some recommendations
Minimum versions to support:

PHP 7.1 and up
WordPress 5.0 and up
WooCommerce 3.6 and up (if applicable)

Code standards:

All code should follow the WordPress PHP code standards.
We recommend installing phpcs and the WordPress-Coding-Standards package with your editor or IDE. If committed code does not pass phpcs, it will not be accepted.
Add code must be documented following the PHP documentation standards.

For an example of a properly coded and documented integration module, see the integration bootstrap plugin,  or for a real-world example look at WP Fusion』s YITH WooCommerce Multi Vendor integration.
Internationalisation:
All strings should be translatable via gettext. The textdomain for WP Fusion is wp-fusion.

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

Sending data to two different CRMs (Intercom Example)

Sending data to two different CRMs (Intercom Example)

#Overview
In the last case study, we explored a solution for a customer who wanted WP Fusion to segment his site users into two different ActiveCampaign accounts, depending on their user role.
In this article, we』ll be dealing with a slightly different problem. This customer uses both Intercom and ActiveCampaign, with ActiveCampaign as his primary CRM (because of the lack of marketing automation features in Intercom).
To keep WP Fusion easy to learn and configure, we don』t natively support connecting your WordPress site to more than one CRM at a time. In fact it』s generally better for your business if you can keep all of your contact data in one system, instead of having it fragmented across several platforms.
#The objective
But there are some exceptions to the rule. In this case, the customer would like to use ActiveCampaign as their primary CRM, but also be able to simultaneously tag contacts in Intercom for certain events.
#The solution
As we discussed in the introduction to WP Fusion』s CRM API, all communication with your CRM is done via interfacing with the wp_fusion()->crm object. In this case, WP Fusion is configured to connect to ActiveCampaign.
But it』s also possible to create and interface with a secondary CRM instance by simply including the appropriate file and passing in your connection details.
You can see the finished code on GitHub here, or continue reading below for a more detailed explanation:
#Setting things up
define( 'INTERCOM_ACCESS_TOKEN', 'aG9tOjyIyZmMxZDc2X2YxMzBfNDBhZV9hOTVjXzRhZDRlNzBiZWMyMzoxOjA=' );

global $intercom;
At the top of the file, we have a place to define your Intercom access token. This will be used later when we first set up the Intercom CRM object.
We also declare $intercom as a global, so that it can be re-used across functions while only having to be initialized once.
#Initializing the Intercom CRM object and configuring the connection
function wpf_connect_to_intercom() {

global $intercom;

if( is_object( $intercom ) ) {
return $intercom;
}

require_once WPF_DIR_PATH . 'includes/crms/intercom/class-intercom.php';

$intercom = new WPF_Intercom;
$intercom->get_params( INTERCOM_ACCESS_TOKEN );

return $intercom;

}

This function is responsible for setting up the Intercom CRM object, so it can be used in the other functions. Here』s how it works:

The first thing we do is check the global $intercom variable. If it』s already been set up, then nothing more needs to be done.
If Intercom hasn』t been set up yet, we include WP Fusion』s Intercom integration class, and enable the API connection by providing it with your access token.
Now the Intercom CRM object is ready to go (it』s that easy!), and you have access to all the available methods we covered earlier when looking at the WP Fusion CRM API.

#A helper function to make it easier to deal with Intercom contact IDs
This second function isn』t essential, but since we』ll be applying tags to Intercom contacts, which have their own contact IDs, I』ve included it to help reduce the number of API calls.
By storing the contact IDs for any users with known Intercom contact records, we won』t have to look them up again when we want to apply tags.
function wpf_get_intercom_contact_id( $user_id ) {

$contact_id = get_user_meta( $user_id, 'wpf_intercom_contact_id', true );

if( ! empty( $contact_id ) ) {

return $contact_id;

} else {

$intercom = wpf_connect_to_intercom();

$user = get_user_by( 'id', $user_id );
$contact_id = $intercom->get_contact_id( $user->user_email );

if( ! is_wp_error( $contact_id ) && $contact_id != false ) {

update_user_meta( $user_id, 'wpf_intercom_contact_id', $contact_id );
return $contact_id;

} else {

return false;

}

}

}

Here』s what we』re doing:

First check the WordPress user』s meta data for an Intercom contact ID. If one is found, we can use it right away.
If there is no contact ID saved, then use the Intercom API to look up the user based on their email address.
If there were no errors, and a contact ID was found, then we update the user』s metadata for future usage.
If no contact exists, return false.

#Sending new user registrations to Intercom, in addition to ActiveCampaign
This next function shows how you can create new Intercom contacts at user registration, after they』ve been added to ActiveCampaign.
function wpf_add_to_intercom( $user_id, $contact_id, $post_data ) {

// Get the Intercom CRM object
$intercom = wpf_connect_to_intercom();

// Check if there's already a contact record in Intercom
$contact_id = $intercom->get_contact_id( $post_data['user_email'] );

if( ! is_wp_error( $contact_id ) && $contact_id == false ) {

// If there's no existing contact, let's create one

// You have to manually specify Intercom internal fields here
// since the WPF Settings >> Contact fields tab is configured for ActiveCampaign

$contact_data = array(
'email' => $post_data['user_email'],
'name' => $post_data['first_name'] . ' ' . $post_data['last_name']
);

// "false" in the second parameter tells it not to use the field mapping set up in the WP Fusion settings
$contact_id = $intercom->add_contact( $contact_data, false );

}

// Save the contact ID for later reference
update_user_meta( $user_id, 'wpf_intercom_contact_id', $contact_id );

// Now you can apply tags
$intercom->apply_tags( array( 'Tag One', 'Tag 2' ), $contact_id );

}

add_action( 'wpf_user_created', 'wpf_add_to_intercom', 10, 3 );

This function is hooked to the 'wpf_user_created' action, which is triggered whenever a user registers on your site and a new CRM contact has been created (in this case in ActiveCampaign).
Here』s what we』re doing:

First we get the Intercom CRM object, either by creating it and configuring the access token, or by retrieving the existing CRM object from the global if it already exists.
Next, we use the wpf_get_intercom_contact_id() function (above) to see if we can get a contact ID for the new user. Even though they just registered on the site, they may already be in Intercom… so we check that to avoid creating duplicate contacts.
If there was no error in looking up the contact ID, and a contact ID doesn』t exist, we proceed to create a new contact:

Because WP Fusion is configured for ActiveCampaign, the settings under WP Fusion >> Contact Fields won』t be accurate here.
In this case I』ve used Intercom』s internal 'email' and 'name' fields to create the contact. But any contact data can be added in this way.

See here for a list of the internal field names for an Intercom contact record.

Finally, we utilize the Intercom CRM object to create the new contact, by calling $intercom->add_contact( $contact_data, false );
This will return the contact ID of the newly created Intercom contact.

After the contact has been created, we save the contact ID to their user meta for future use.
And now, with a contact ID available, you can apply any tags that you』d like to be added at registration.

#Applying tags in Intercom when they』re applied in ActiveCampaign
This function follows a similar format as the previous one, but this time we』re using the 'wpf_tags_applied' hook, which is fired whenever a tag has been applied by WP Fusion.
In this case, any tags that have been applied in ActiveCampaign will be applied simultaneously in Intercom.
function wpf_apply_tags_in_intercom( $user_id, $tags ) {

$intercom = wpf_connect_to_intercom();

$contact_id = wpf_get_intercom_contact_id( $user_id );

if( $contact_id !== false ) {
$intercom->apply_tags( $tags, $contact_id );
}

}

add_action( 'wpf_tags_applied', 'wpf_apply_tags_in_intercom', 10, 2 );

Here』s what we』re doing:

As before, we』re either creating or retrieving an existing CRM object to make our requests to.
We look up the contact ID, using the wpf_get_intercom_contact_id() function from earlier.
If the user does have a contact ID in Intercom, we use the CRM object to apply the relevant tags.

#In summary
Obviously this is a very niche example, and it could be customized to be even more specific. For example, to only add contacts to Intercom when a Gravity Form is submitted, or to only tag contacts in Intercom when a lesson is marked complete.
But it hopefully demonstrates how WP Fusion, as a framework, can be extended to accommodate even the most complex business requirements. And the methods we』ve explored here should serve as a foundation for any more specific scenarios you』d like to implement.
And, importantly, WP Fusion gives you a system where these kinds of complex API operations can be executed with a minimal amount of custom code. If you return to view the full Gist on GitHub, you』ll see we did all of this in just 51 lines of code.
 
We really enjoy coming up with these kinds of solutions for our customers. If you』re interested in discussing implementation ideas for your own project, send us a message, we』d love to hear from you!
 

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

Creating Custom CRM Modules

Creating Custom CRM Modules

#Overview
WP Fusion can be extended to connect to additional CRMs or other contact databases outside of our included integrations.
WP Fusion』s integration modules are standardized across all our supported CRMs. Once the integration module is complete, it allows 100+ of the most popular WordPress plugins to communicate bidirectionally with your CRM or marketing automation platform. In most cases this is significantly faster and cheaper than developing custom CRM integrations one plugin at a time.
For a bootstrap to get you started, download this example plugin from GitHub.
#Requirements
While WP Fusion is pretty flexible, it does not work with all platforms.
For WP Fusion to work properly, your CRM or marketing automation tool must at minimum have API methods for:

Get all available tags (or 「segments」, 「groups」, 「static lists」, aka whatever is going to be used for segmenting contacts)
Get all available contact custom fields (or 「attributes」, 「properties」, etc.)
Search for a contact ID by email address
Load the tags for a contact, by ID
Apply tags to a contact, by contact ID
Remove tags from a contact, by contact ID
Add a new contact, and return a contact ID
Update a contact by ID
Load a contact and all their properties, by contact ID
(optional) Search for contacts by tag name or ID. This is required for the Import Tool to work.

#Setting up a custom CRM module
First download the starter plugin. Then do a find and replace (case sensitive) on two strings:

「custom」: This is the slug for the integration. Change it to something like 「my_crm_name」
「Custom」: This is the title for the integration, and is also used for the class names. Change it to something like 「My_CRM_Name」

Also change the file names to reflect the new slug.
Then you can go through each of the methods in class-wpf-custom.php and update them with the API calls specific to your integration, following the guide here.
#File structure
There are three important files:

wp-fusion-custom-crm.php: This is the base plugin file. It defines the plugin name, loads the dependencies, and adds the custom CRM to the dropdown in WP Fusion』s setup tab.
class-wpf-custom.php: This is the base class for custom module. It includes all API calls and methods relating to sending and retrieving data.
class-wpf-custom-admin.php: This file is only loaded in the WordPress admin. It defines the settings required to establish a connection, and contains any additional admin functionality.

#Notes
$supports: This variable declares some CRM-specific features to other aspects of WP Fusion. If the CRM supports 「add_tags」, then WP Fusion』s tag dropdowns will support typing in a new tag name on the fly. If the platform uses tag IDs, or requires tags to be registered before they』re used, don』t declare support for 「add_tags」.
「add_fields」 works similarly to 「add_tags」. If the CRM supports 「add_fields」, users will be able to enter custom fields on the fly. Don』t use this if the platform requires field IDs.
#Contribution guidelines
For contribution and style guidelines, see this doc.

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No

The WP Fusion CRM API

The WP Fusion CRM API

#Overview
WP Fusion takes a completely original approach to connecting WordPress to our supported CRMs and marketing automation systems. There is no other plugin available that is as flexible or extensible.
Normally, with a plugin like Gravity Forms (for example), you would download one of several available add-ons that connect Gravity Forms to your CRM of choice, like the ActiveCampaign add-on or AgileCRM add-on. These are created from scratch and use code unique to the API in question.
Before WP Fusion, many sites would have to use several different plugins to get a basic level of integration with their CRM. An ActiveCampaign user might need ActiveCampaign add-on for Gravity Forms, ActiveWoo to send WooCommerce order data, and the official ActiveCampaign plugin to enable site tracking and embedding forms.
This introduces a lot of unnecessary overlap, with the ActiveCampaign SDK being included multiple times, and redundant API calls being sent.
#How we solved that problem
When designing WP Fusion, we realized that all of these CRM systems offer the same basic functionality: adding new contacts, updating existing contacts, applying and removing tags, and loading contact data from the CRM.
So with that in mind, we created a wrapper class for each CRM, with a standardized set of functions to send and receive data from WordPress, then reformat it according to the rules of each API. Every one of our integrations has at least the following methods:
#connect()
wp_fusion()->crm->connect( $auth = null, $test = false );
The connect() function is called by all the member functions in the class to initialize the connection to the CRM. It』s not necessary to use this in your code, but it can be used to validate an API key or OAuth token by setting $test to true.
Parameters:

$auth (string) (Optional) The API key or other authorization code required to connect. There may be more than one parameter depending on the CRM. If this is left blank, WP Fusion will use the authentication data you entered on the original setup page.
$test (bool) (Optional) If set to true, WP Fusion will also verify the connection by attempting to make an API call.

Return values:

true (bool) If the connection was successful
$error (WP_Error object) If the connection was unsuccessful

#sync_tags()
wp_fusion()->crm->sync_tags();
Loads all available tags from the CRM and updates the available tags in the tag dropdowns for WP Fusion.
Return values:

$tags (array) An array of tags in the CRM
$error (WP_Error object) If the API call failed

#sync_crm_fields()
wp_fusion()->crm->sync_crm_fields();
Loads all available fields and custom fields from the CRM and updates the available fields in the dropdowns for WP Fusion.
Return values:

$crm_fields (array) An array of available fields in the CRM
$error (WP_Error object) If the API call failed

#get_contact_id()
wp_fusion()->crm->get_contact_id( $email_address );
Looks up a contact ID in the CRM by their email address.
Parameters:

$email_address (string) The email address to search for a contact by

Return values:

$contact_id (int) A contact ID for that email address
false (bool) If no contact ID was found with that email address
$error (WP_Error object) If the API call failed

#get_tags()
wp_fusion()->crm->get_tags( $contact_id );
Loads a contact』s tags from the CRM.
Parameters:

$contact_id (int) The contact ID to load tags for

Return values:

$tags (array) An array of tag IDs for the contact (or an empty array if no tags were found)
$error (WP_Error object) If the API call failed

#apply_tags()
wp_fusion()->crm->apply_tags( $tags, $contact_id );
Applies one or more tags to a contact.
Parameters:

$tags (array) An array of tags to apply
$contact_id (int) The contact ID to apply the tags to

Return values:

true (bool) The tags were successfully removed
$error (WP_Error object) If the API call failed

#remove_tags()
wp_fusion()->crm->remove_tags( $tags, $contact_id );
Removes one or more tags to a contact.
Parameters:

$tags (array) An array of tags to remove
$contact_id (int) The contact ID to remove the tags from

Return values:

true (bool) The tags were successfully removed
$error (WP_Error object) If the API call failed

#add_contact()
wp_fusion()->crm->add_contact( $contact_data, $map_meta_fields = true );
Adds a new contact to the CRM.
Parameters:

$contact_data (array) An associative array containing the data for the new contact, with the WordPress field as the key and the data as the value, like array( 'user_email' => '[email protected]' );
$map_meta_fields (bool) If set to true, WP Fusion will convert the field keys from WordPress meta keys into the field names in the CRM. Set to false to bypass this conversion.

Return values:

$contact_id (int) The contact ID for the newly created contact
$error (WP_Error object) If the API call failed or the data was rejected

#update_contact()
wp_fusion()->crm->update_contact( $contact_id, $contact_data, $map_meta_fields = true );
Updates a contact in the CRM.
Parameters:

$contact_id (int) The contact ID to update
$contact_data (array) An associative array containing the update data, in the same format as add_contact();
$map_meta_fields (bool) If set to true, WP Fusion will convert the field keys from WordPress meta keys into the field names in the CRM. Set to false to bypass this conversion.

Return values:

true (bool) The contact was successfully updated
$error (WP_Error object) If the API call failed or the data was rejected

#load_contact()
wp_fusion()->crm->load_contact( $contact_id );
Loads the contact record for a contact ID and returns an associative array of WordPress field / value pairs, based on the WP Fusion 「Contact Field」 settings.
Parameters:

$contact_id (int) The contact to load

Return values:

$user_meta (array) Array containing the contact data
$error (WP_Error object) If the API call failed or the contact was not found

#load_contacts()
wp_fusion()->crm->load_contacts( $tag_id );
Searches the CRM for any contacts with the specified tag and returns an array of contact IDs.
Parameters:

$tag (int or string) The tag to search by

Return values:

$contact_ids (array) Array of contact IDs returned by the search. Will be an empty array if no results were found
$error (WP_Error object) If the API call failed

See the next section for more information on how to utilize WP Fusion』s core helper functions while interfacing with your CRM.

#Was this helpful?

Let us know if you liked the post. That』s the only way we can improve.

Yes

No