Skip to content

Commit

Permalink
feat(ras): enable custom contact metadata prefixes (#2249)
Browse files Browse the repository at this point in the history
* feat(ras): enable custom contact metadata prefixes

* feat: make metadata prefix filterable

* refactor: more efficient getter method and filtering

* fix: remove extra condition from merge conflict resolution

* fix: new prefix for the Mailchimp connector

* chore: add help text explaining why the prefix is required

---------

Co-authored-by: Miguel Peixe <miguel.peixe@automattic.com>
  • Loading branch information
dkoo and miguelpeixe authored Jan 30, 2023
1 parent 0031b89 commit e9843e4
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 102 deletions.
8 changes: 8 additions & 0 deletions assets/wizards/engagement/views/reader-activation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,14 @@ export default withWizardScreen( () => {
) }
{ ...getSharedProps( 'sync_esp_delete' ) }
/>
<TextControl
label={ __( 'Metadata field prefix', 'newspack' ) }
help={ __(
'A string to prefix metadata fields attached to each contact synced to the ESP. Required to ensure that metadata field names are unique. Default: NP_',
'newspack'
) }
{ ...getSharedProps( 'metadata_prefix', 'text' ) }
/>
{ isActiveCampaign && (
<ActiveCampaign
value={ { masterList: config.active_campaign_master_list } }
Expand Down
18 changes: 11 additions & 7 deletions includes/data-events/connectors/class-mailchimp.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,16 @@ private static function put( $email, $data = [] ) {
* @param int $client_id ID of the client that triggered the event.
*/
public static function reader_registered( $timestamp, $data, $client_id ) {
$metadata = [
'NP_Account' => $data['user_id'],
$prefix = Newspack_Newsletters::get_metadata_prefix();
$account_key = Newspack_Newsletters::get_metadata_key( 'account' );
$metadata = [
$account_key => $data['user_id'],
];
if ( isset( $data['metadata']['current_page_url'] ) ) {
$metadata['NP_Registration Page'] = $data['metadata']['current_page_url'];
$metadata[ $prefix . 'Registration Page' ] = $data['metadata']['current_page_url'];
}
if ( isset( $data['metadata']['registration_method'] ) ) {
$metadata['NP_Registration Method'] = $data['metadata']['registration_method'];
$metadata[ $prefix . 'Registration Method' ] = $data['metadata']['registration_method'];
}
self::put( $data['email'], $metadata );
}
Expand Down Expand Up @@ -203,7 +205,7 @@ public static function donation_new( $timestamp, $data, $client_id ) {

// Remove "product name" from metadata, we'll use
// 'donation_subscription_new' action for this data.
unset( $metadata[ $keys['product_name'] ] );
unset( $metadata[ Newspack_Newsletters::get_metadata_key( 'product_name' ) ] );

self::put( $email, $metadata );
}
Expand All @@ -219,14 +221,16 @@ public static function donation_subscription_new( $timestamp, $data, $client_id
if ( empty( $data['platform_data']['order_id'] ) ) {
return;
}
$account_key = Newspack_Newsletters::get_metadata_key( 'account' );
$metadata = [
'NP_Account' => $data['user_id'],
$account_key => $data['user_id'],
];
$order_id = $data['platform_data']['order_id'];
$product_id = Donations::get_order_donation_product_id( $order_id );
$product_name = get_the_title( $product_id );

$metadata['NP_Product Name'] = $product_name;
$key = Newspack_Newsletters::get_metadata_key( 'product_name' );
$metadata[ $key ] = $product_name;

self::put( $data['email'], $metadata );
}
Expand Down
135 changes: 100 additions & 35 deletions includes/plugins/class-newspack-newsletters.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,36 @@
* Main class.
*/
class Newspack_Newsletters {
const METADATA_DATE_FORMAT = 'Y-m-d';
const METADATA_PREFIX = 'NP_';
const METADATA_DATE_FORMAT = 'Y-m-d';
const METADATA_PREFIX = 'NP_';
const METADATA_PREFIX_OPTION = '_newspack_metadata_prefix';


/**
* Metadata keys map for Reader Activation.
*
* @var array
*/
public static $metadata_keys = [
'account' => self::METADATA_PREFIX . 'Account',
'registration_date' => self::METADATA_PREFIX . 'Registration Date',
'connected_account' => self::METADATA_PREFIX . 'Connected Account',
'signup_page' => self::METADATA_PREFIX . 'Signup Page',
'signup_page_utm' => self::METADATA_PREFIX . 'Signup UTM: ',
'newsletter_selection' => self::METADATA_PREFIX . 'Newsletter Selection',
'account' => 'Account',
'registration_date' => 'Registration Date',
'connected_account' => 'Connected Account',
'signup_page' => 'Signup Page',
'signup_page_utm' => 'Signup UTM: ',
'newsletter_selection' => 'Newsletter Selection',
// Payment-related.
'membership_status' => self::METADATA_PREFIX . 'Membership Status',
'payment_page' => self::METADATA_PREFIX . 'Payment Page',
'payment_page_utm' => self::METADATA_PREFIX . 'Payment UTM: ',
'sub_start_date' => self::METADATA_PREFIX . 'Current Subscription Start Date',
'sub_end_date' => self::METADATA_PREFIX . 'Current Subscription End Date',
'billing_cycle' => self::METADATA_PREFIX . 'Billing Cycle',
'recurring_payment' => self::METADATA_PREFIX . 'Recurring Payment',
'last_payment_date' => self::METADATA_PREFIX . 'Last Payment Date',
'last_payment_amount' => self::METADATA_PREFIX . 'Last Payment Amount',
'product_name' => self::METADATA_PREFIX . 'Product Name',
'next_payment_date' => self::METADATA_PREFIX . 'Next Payment Date',
'total_paid' => self::METADATA_PREFIX . 'Total Paid',
'membership_status' => 'Membership Status',
'payment_page' => 'Payment Page',
'payment_page_utm' => 'Payment UTM: ',
'sub_start_date' => 'Current Subscription Start Date',
'sub_end_date' => 'Current Subscription End Date',
'billing_cycle' => 'Billing Cycle',
'recurring_payment' => 'Recurring Payment',
'last_payment_date' => 'Last Payment Date',
'last_payment_amount' => 'Last Payment Amount',
'product_name' => 'Product Name',
'next_payment_date' => 'Next Payment Date',
'total_paid' => 'Total Paid',
];

/**
Expand All @@ -54,6 +56,69 @@ public static function init() {
}
}

/**
* Fetch the prefix for synced metadata fields.
* Default is NP_ but it can be configured in the Reader Activation settings page.
*
* @return string
*/
public static function get_metadata_prefix() {
$prefix = \get_option( self::METADATA_PREFIX_OPTION, self::METADATA_PREFIX );

// Guard against empty strings and falsy values.
if ( empty( $prefix ) ) {
return self::METADATA_PREFIX;
}

/**
* Filters the string used to prefix custom fields synced to Newsletter ESPs.
*
* @param string $prefix Prefix to prepend the field name.
*/
return apply_filters( 'newspack_ras_metadata_prefix', $prefix );
}

/**
* Update the prefix for synced metadata fields.
*
* @param string $prefix Value to set.
*
* @return boolean True if updated, false otherwise.
*/
public static function update_metadata_prefix( $prefix ) {
if ( empty( $prefix ) ) {
$prefix = self::METADATA_PREFIX;
}

return \update_option( self::METADATA_PREFIX_OPTION, $prefix );
}

/**
* Given a field name, prepend it with the metadata field prefix.
*
* @param string $key Metadata field to fetch.
*
* @return string Prefixed field name.
*/
public static function get_metadata_key( $key ) {
if ( ! isset( self::$metadata_keys[ $key ] ) ) {
return false;
}

$prefix = self::get_metadata_prefix();
$name = self::$metadata_keys[ $key ];
$key = $prefix . $name;

/**
* Filters the full, prefixed field name of each custom field synced to the ESP.
*
* @param string $key Full, prefixed key.
* @param string $prefix The prefix part of the key.
* @param string $name The unprefixed part of the key.
*/
return apply_filters( 'newspack_ras_metadata_key', $key, $prefix, $name );
}

/**
* Update content metadata after a contact's lists are updated.
*
Expand Down Expand Up @@ -94,7 +159,7 @@ public static function contact_data( $contact, $selected_list_ids, $provider ) {
case 'active_campaign':
$metadata = [];
if ( is_user_logged_in() ) {
$metadata[ self::$metadata_keys['account'] ] = get_current_user_id();
$metadata[ self::get_metadata_key( 'account' ) ] = get_current_user_id();
}

// Translate list IDs to list names and store as metadata, if lists are supplied.
Expand All @@ -113,7 +178,7 @@ public static function contact_data( $contact, $selected_list_ids, $provider ) {
}
}
// Note: this field will be overwritten every time it's updated.
$metadata[ self::$metadata_keys['newsletter_selection'] ] = implode( ', ', $lists_names );
$metadata[ self::get_metadata_key( 'newsletter_selection' ) ] = implode( ', ', $lists_names );
}
}
} catch ( \Throwable $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
Expand All @@ -131,54 +196,54 @@ public static function contact_data( $contact, $selected_list_ids, $provider ) {

$is_new_contact = ! $contact['existing_contact_data'];
// If the contact exists, but has no account metadata (or any metadata), treat it as a new contact.
$metadata_account_field_formatted = strtoupper( self::$metadata_keys['account'] );
$metadata_account_field_formatted = strtoupper( self::get_metadata_key( 'account' ) );
if ( $contact['existing_contact_data'] && ! isset( $contact['existing_contact_data']['metadata'], $contact['existing_contact_data']['metadata'][ $metadata_account_field_formatted ] ) ) {
$is_new_contact = true;
}
if ( $is_new_contact ) {
$contact['metadata'][ self::$metadata_keys['registration_date'] ] = gmdate( self::METADATA_DATE_FORMAT );
$metadata[ self::$metadata_keys['signup_page'] ] = $current_page_url;
$contact['metadata'][ self::get_metadata_key( 'registration_date' ) ] = gmdate( self::METADATA_DATE_FORMAT );
$metadata[ self::get_metadata_key( 'signup_page' ) ] = $current_page_url;

// Capture UTM params.
foreach ( [ 'source', 'medium', 'campaign' ] as $value ) {
$param = 'utm_' . $value;
if ( isset( $current_page_url_params[ $param ] ) ) {
$metadata[ self::$metadata_keys['signup_page_utm'] . $value ] = sanitize_text_field( $current_page_url_params[ $param ] );
$metadata[ self::get_metadata_key( 'signup_page_utm' ) . $value ] = sanitize_text_field( $current_page_url_params[ $param ] );
}
}
}

// If the membership status is to be switched from recurring to non-recurring, ignore this change.
if ( $contact['existing_contact_data'] && isset( $contact['metadata'][ self::$metadata_keys['membership_status'] ], $existing_metadata[ self::$metadata_keys['membership_status'] ] ) ) {
if ( $contact['existing_contact_data'] && isset( $contact['metadata'][ self::get_metadata_key( 'membership_status' ) ], $existing_metadata[ self::get_metadata_key( 'membership_status' ) ] ) ) {
$existing_metadata = $contact['existing_contact_data']['metadata'];
$becomes_once_donor = Stripe_Connection::ESP_METADATA_VALUES['once_donor'] === $contact['metadata'][ self::$metadata_keys['membership_status'] ];
$becomes_once_donor = Stripe_Connection::ESP_METADATA_VALUES['once_donor'] === $contact['metadata'][ self::get_metadata_key( 'membership_status' ) ];
$is_recurring_donor = in_array(
$existing_metadata[ self::$metadata_keys['membership_status'] ],
$existing_metadata[ self::get_metadata_key( 'membership_status' ) ],
[
Stripe_Connection::ESP_METADATA_VALUES['monthly_donor'],
Stripe_Connection::ESP_METADATA_VALUES['yearly_donor'],
]
);
if ( $becomes_once_donor && $is_recurring_donor ) {
unset( $contact['metadata'][ self::$metadata_keys['membership_status'] ] );
unset( $contact['metadata'][ self::get_metadata_key( 'membership_status' ) ] );
}
}

if ( isset( $contact['metadata'] ) ) {
if ( isset( $contact['metadata'][ self::$metadata_keys['last_payment_amount'] ] ) ) {
$metadata[ self::$metadata_keys['payment_page'] ] = $current_page_url;
if ( isset( $contact['metadata'][ self::get_metadata_key( 'last_payment_amount' ) ] ) ) {
$metadata[ self::get_metadata_key( 'payment_page' ) ] = $current_page_url;
foreach ( [ 'source', 'medium', 'campaign' ] as $value ) {
$param = 'utm_' . $value;
if ( isset( $current_page_url_params[ $param ] ) ) {
$metadata[ self::$metadata_keys['payment_page_utm'] . $value ] = sanitize_text_field( $current_page_url_params[ $param ] );
$metadata[ self::get_metadata_key( 'payment_page_utm' ) . $value ] = sanitize_text_field( $current_page_url_params[ $param ] );
}
}
}

if ( isset( $contact['metadata']['registration_method'] ) ) {
$registration_method = $contact['metadata']['registration_method'];
if ( in_array( $registration_method, Reader_Activation::SSO_REGISTRATION_METHODS ) ) {
$contact['metadata'][ self::$metadata_keys['connected_account'] ] = $registration_method;
$contact['metadata'][ self::get_metadata_key( 'connected_account' ) ] = $registration_method;
}
}
}
Expand All @@ -191,7 +256,7 @@ public static function contact_data( $contact, $selected_list_ids, $provider ) {

// Ensure only the prefixed metadata is passed along to the ESP.
foreach ( $contact['metadata'] as $key => $value ) {
if ( strpos( $key, self::METADATA_PREFIX ) !== 0 ) {
if ( strpos( $key, self::get_metadata_prefix() ) !== 0 ) {
unset( $contact['metadata'][ $key ] );
}
}
Expand Down
5 changes: 5 additions & 0 deletions includes/reader-activation/class-reader-activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ private static function get_settings_config() {
'terms_text' => __( 'By signing up, you agree to our Terms and Conditions.', 'newspack' ),
'terms_url' => '',
'sync_esp' => true,
'metadata_prefix' => Newspack_Newsletters::get_metadata_prefix(),
'sync_esp_delete' => true,
'active_campaign_master_list' => '',
'mailchimp_audience_id' => '',
Expand Down Expand Up @@ -251,6 +252,10 @@ public static function update_setting( $key, $value ) {
if ( is_bool( $value ) ) {
$value = intval( $value );
}
if ( 'metadata_prefix' === $key ) {
return Newspack_Newsletters::update_metadata_prefix( $value );
}

return \update_option( self::OPTIONS_PREFIX . $key, $value );
}

Expand Down
Loading

0 comments on commit e9843e4

Please sign in to comment.