Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions includes/reader-activation/class-reader-activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ final class Reader_Activation {
const WITHOUT_PASSWORD = 'np_reader_without_password';
const REGISTRATION_METHOD = 'np_reader_registration_method';
const REGISTRATION_PAGE = 'np_reader_registration_page';
const REGISTRATION_UTM_SOURCE = 'np_reader_registration_utm_source';
const REGISTRATION_UTM_MEDIUM = 'np_reader_registration_utm_medium';
const REGISTRATION_UTM_CAMPAIGN = 'np_reader_registration_utm_campaign';
const CONNECTED_ACCOUNT = 'np_reader_connected_account';
const READER_SAVED_GENERIC_DISPLAY_NAME = 'np_reader_saved_generic_display_name';

Expand Down Expand Up @@ -2104,6 +2107,18 @@ public static function process_auth_form() {
$redirect_url = isset( $_POST['redirect_url'] ) ? \esc_url_raw( $_POST['redirect_url'] ) : '';
// phpcs:enable

// Capture UTM params before reconstructing the URL (which drops query params).
$registration_utm = [];
if ( ! empty( $current_page_url['query'] ) ) {
$query_params = [];
\wp_parse_str( $current_page_url['query'], $query_params );
foreach ( [ 'utm_source', 'utm_medium', 'utm_campaign' ] as $utm_param ) {
if ( ! empty( $query_params[ $utm_param ] ) ) {
$registration_utm[ $utm_param ] = \sanitize_text_field( $query_params[ $utm_param ] );
}
}
}

if ( ! empty( $current_page_url['path'] ) ) {
$current_page_url = \esc_url( \home_url( $current_page_url['path'] ) );
}
Expand Down Expand Up @@ -2210,6 +2225,9 @@ public static function process_auth_form() {
if ( ! empty( $current_page_url ) ) {
$metadata['current_page_url'] = $current_page_url;
}
if ( ! empty( $registration_utm ) ) {
$metadata['registration_utm'] = $registration_utm;
}

$user_id = self::register_reader( $email, '', true, $metadata );
if ( false === $user_id ) {
Expand Down Expand Up @@ -2431,6 +2449,33 @@ public static function register_reader( $email, $display_name = '', $authenticat
\update_user_meta( $user_id, self::REGISTRATION_PAGE, $metadata['current_page_url'] );
}

// Save registration UTM params.
$utm_keys = [
'utm_source' => self::REGISTRATION_UTM_SOURCE,
'utm_medium' => self::REGISTRATION_UTM_MEDIUM,
'utm_campaign' => self::REGISTRATION_UTM_CAMPAIGN,
];
// Prefer explicitly passed UTM params (from process_auth_form).
$registration_utm = $metadata['registration_utm'] ?? [];
// Fallback: parse from the registration page URL if it has query params.
if ( empty( $registration_utm ) && ! empty( $metadata['current_page_url'] ) ) {
$parsed = \wp_parse_url( $metadata['current_page_url'] );
if ( ! empty( $parsed['query'] ) ) {
$query_params = [];
\wp_parse_str( $parsed['query'], $query_params );
foreach ( $utm_keys as $param => $meta_key ) {
if ( ! empty( $query_params[ $param ] ) ) {
$registration_utm[ $param ] = $query_params[ $param ];
}
}
}
}
foreach ( $utm_keys as $param => $meta_key ) {
if ( ! empty( $registration_utm[ $param ] ) ) {
\update_user_meta( $user_id, $meta_key, \sanitize_text_field( $registration_utm[ $param ] ) );
}
}

/**
* Action after registering and authenticating a reader.
*
Expand Down
4 changes: 2 additions & 2 deletions includes/reader-activation/sync/class-contact-metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ public function get_full_name() {
}

/**
* Format a date string to MM/DD/YYYY.
* Format a date string.
*
* @param string $date_string Date string from WooCommerce.
* @param string $date_string Date string.
* @return string Formatted date or empty string.
*/
protected function format_date( $date_string ) {
Expand Down
15 changes: 8 additions & 7 deletions includes/reader-activation/sync/class-contact-sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,6 @@ public static function sync( $contact, $context = '', $existing_contact = null )
}
}

// Added logging here to more easily monitor integration sync data. Can be removed once integrations are released.
if ( 'legacy' !== Metadata::get_version() ) {
Logger::log( sprintf( 'Syncing contact %s for context "%s".', $contact['email'] ?? 'unknown', $context ) );
Logger::log( $contact );
}

return self::push_to_integrations( $contact, $context, $existing_contact );
}

Expand Down Expand Up @@ -178,7 +172,14 @@ private static function push_to_integrations( $contact, $context, $existing_cont

foreach ( $integrations as $integration_id => $integration ) {
$integration_contact = $integration->prepare_contact( $contact );
$result = $integration->push_contact_data( $integration_contact, $context, $existing_contact );

// Added logging here to more easily monitor integration sync data. Can be removed once integrations are released.
if ( 'legacy' !== Metadata::get_version() ) {
Logger::log( sprintf( 'Syncing contact %s for integration %s with context "%s".', $integration_contact['email'] ?? 'unknown', $integration_id, $context ) );
Logger::log( $integration_contact );
}

$result = $integration->push_contact_data( $integration_contact, $context, $existing_contact );
if ( \is_wp_error( $result ) ) {
/**
* Fires when a contact sync fails on the original attempt (before retries).
Expand Down
5 changes: 3 additions & 2 deletions includes/reader-activation/sync/class-metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ protected static function get_metadata_classes() {
];
} else {
$classes = [
'Profile',
'Identity',
'Registration',
'Engagement',
'Subscription',
'Donation',
'Content_Gate',
'Financial',
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Newspack\Reader_Activation\Sync\Contact_Metadata;

use Newspack\Reader_Data;
use Newspack\Reader_Activation\Sync\Contact_Metadata;

defined( 'ABSPATH' ) || exit;
Expand All @@ -16,6 +17,13 @@
*/
class Engagement extends Contact_Metadata {

/**
* Cache for the latest completed order.
*
* @var \WC_Order|null|false False means unresolved.
*/
private $latest_order_cache = false;

/**
* Whether or not the metadata fields of this class are available to be synced.
*
Expand Down Expand Up @@ -43,7 +51,6 @@ public static function get_fields() {
return [
'First_Visit_Date' => 'First Visit Date',
'Last_Active' => 'Last Active',
'Articles_Read' => 'Articles Read',
'Paywall_Hits' => 'Paywall Hits',
'Favorite_Categories' => 'Favorite Categories',
'Payment_Page' => 'Payment Page',
Expand All @@ -60,6 +67,141 @@ public static function get_fields() {
* @return array
*/
public function get_metadata() {
return [];
if ( ! $this->user ) {
return [];
}

$order = $this->get_latest_order();

return [
'First_Visit_Date' => $this->format_reader_data_timestamp( 'first_visit_date' ),
'Last_Active' => $this->format_reader_data_timestamp( 'last_active' ),
'Paywall_Hits' => $this->get_reader_data_int( 'paywall_hits' ),
'Favorite_Categories' => $this->get_favorite_categories(),
'Payment_Page' => $this->get_payment_page( $order ),
'Payment_UTM_Source' => $this->get_order_utm( $order, 'source' ),
'Payment_UTM_Medium' => $this->get_order_utm( $order, 'medium' ),
'Payment_UTM_Campaign' => $this->get_order_utm( $order, 'campaign' ),
'Total_Paid' => $this->customer ? $this->customer->get_total_spent() : '',
];
}

/**
* Format a reader data store timestamp (JS milliseconds) into a date string.
*
* @param string $key Reader data store key.
* @return string Formatted date or empty string.
*/
private function format_reader_data_timestamp( $key ) {
$value = Reader_Data::get_data( $this->user->ID, $key );
if ( empty( $value ) || ! is_numeric( $value ) ) {
return '';
}
// Reader data timestamps are JS milliseconds.
return $this->format_date( gmdate( 'Y-m-d H:i:s', intval( $value ) / 1000 ) );
}

/**
* Get an integer value from the reader data store.
*
* @param string $key Reader data store key.
* @return int
*/
private function get_reader_data_int( $key ) {
$value = Reader_Data::get_data( $this->user->ID, $key );
return is_numeric( $value ) ? (int) $value : 0;
}

/**
* Get favorite categories as a comma-separated string of category names.
*
* @return string
*/
private function get_favorite_categories() {
$category_ids = Reader_Data::get_data( $this->user->ID, 'favorite_categories' );
if ( is_string( $category_ids ) ) {
$category_ids = json_decode( $category_ids, true );
}
if ( empty( $category_ids ) || ! is_array( $category_ids ) ) {
return '';
}
$names = [];
foreach ( $category_ids as $cat_id ) {
$term = \get_term( (int) $cat_id, 'category' );
if ( $term && ! \is_wp_error( $term ) ) {
$names[] = $term->name;
}
}
return implode( ',', $names );
}

/**
* Get the most recent completed order for the current user.
*
* @return \WC_Order|null
*/
private function get_latest_order() {
if ( false !== $this->latest_order_cache ) {
return $this->latest_order_cache;
}

$this->latest_order_cache = null;

if ( ! $this->user || ! function_exists( 'wc_get_orders' ) ) {
return null;
}

$orders = \wc_get_orders(
[
'customer_id' => $this->user->ID,
'status' => [ 'wc-completed' ],
'limit' => 1,
'order' => 'DESC',
'orderby' => 'date',
'return' => 'objects',
]
);

if ( ! empty( $orders ) ) {
$this->latest_order_cache = $orders[0];
}

return $this->latest_order_cache;
}

/**
* Get the payment page URL from an order.
*
* @param \WC_Order|null $order Order object.
* @return string
*/
private function get_payment_page( $order ) {
if ( ! $order ) {
return '';
}
$referer = $order->get_meta( '_newspack_referer' );
if ( ! empty( $referer ) ) {
return $referer;
}
return function_exists( 'wc_get_checkout_url' ) ? \wc_get_checkout_url() : '';
}

/**
* Extract a UTM parameter from an order.
*
* @param \WC_Order|null $order Order object.
* @param string $param UTM parameter name (e.g. 'source', 'medium', 'campaign').
* @return string
*/
private function get_order_utm( $order, $param ) {
if ( ! $order ) {
return '';
}
$utm = $order->get_meta( 'utm' );
if ( ! empty( $utm ) && is_array( $utm ) && isset( $utm[ $param ] ) ) {
return $utm[ $param ];
}
$value = $order->get_meta( 'utm_' . $param );
return ! empty( $value ) ? $value : '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Newspack\Reader_Activation\Sync\Contact_Metadata;

use Newspack\Reader_Activation;
use Newspack\Reader_Activation\Sync\Contact_Metadata;

defined( 'ABSPATH' ) || exit;
Expand Down Expand Up @@ -57,6 +58,20 @@ public static function get_fields() {
* @return array
*/
public function get_metadata() {
return [];
if ( ! $this->user ) {
return [];
}

$roles = $this->user->roles;

return [
'first_name' => $this->user->first_name,
'last_name' => $this->user->last_name,
'email' => $this->user->user_email,
'Account' => (string) $this->user->ID,
'User_Role' => ! empty( $roles ) ? reset( $roles ) : '',
'verified' => (bool) \get_user_meta( $this->user->ID, Reader_Activation::EMAIL_VERIFIED, true ),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use Reader_Activation::is_reader_verified instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Updated in 3afb47d

'Connected_Account' => (string) \get_user_meta( $this->user->ID, Reader_Activation::CONNECTED_ACCOUNT, true ),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Newspack\Reader_Activation\Sync\Contact_Metadata;

use Newspack\Reader_Activation;
use Newspack\Reader_Activation\Sync\Contact_Metadata;

defined( 'ABSPATH' ) || exit;
Expand Down Expand Up @@ -56,6 +57,35 @@ public static function get_fields() {
* @return array
*/
public function get_metadata() {
return [];
if ( ! $this->user ) {
return [];
}

return [
'Registration_Date' => $this->format_date( $this->user->user_registered ),
'Registration_Page' => (string) \get_user_meta( $this->user->ID, Reader_Activation::REGISTRATION_PAGE, true ),
'Registration_Strategy' => (string) \get_user_meta( $this->user->ID, Reader_Activation::REGISTRATION_METHOD, true ),
'Registration_UTM_Source' => $this->get_registration_utm( 'utm_source' ),
'Registration_UTM_Medium' => $this->get_registration_utm( 'utm_medium' ),
'Registration_UTM_Campaign' => $this->get_registration_utm( 'utm_campaign' ),
];
}

/**
* Get a registration UTM parameter from user meta.
*
* @param string $param UTM parameter name (e.g. 'utm_source').
* @return string UTM value or empty string.
*/
private function get_registration_utm( $param ) {
$meta_keys = [
'utm_source' => Reader_Activation::REGISTRATION_UTM_SOURCE,
'utm_medium' => Reader_Activation::REGISTRATION_UTM_MEDIUM,
'utm_campaign' => Reader_Activation::REGISTRATION_UTM_CAMPAIGN,
];
if ( ! isset( $meta_keys[ $param ] ) ) {
return '';
}
return (string) \get_user_meta( $this->user->ID, $meta_keys[ $param ], true );
}
}
10 changes: 5 additions & 5 deletions src/blocks/reader-registration/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -512,11 +512,11 @@ function process_form() {
}

// reCAPTCHA test.
$current_page_url = \wp_parse_url( \wp_get_raw_referer() );
if ( ! empty( $current_page_url['path'] ) ) {
$current_page_url = \esc_url( \home_url( $current_page_url['path'] ) );
}
if ( apply_filters( 'newspack_recaptcha_verify_captcha', Recaptcha::can_use_captcha(), $current_page_url, 'registration_block' ) ) {
$raw_referer = \wp_get_raw_referer();
$parsed_referer = \wp_parse_url( $raw_referer );
$current_page_url = ! empty( $parsed_referer['path'] ) ? \esc_url( $raw_referer ) : $raw_referer;
$recaptcha_url = ! empty( $parsed_referer['path'] ) ? \esc_url( \home_url( $parsed_referer['path'] ) ) : $current_page_url;
if ( apply_filters( 'newspack_recaptcha_verify_captcha', Recaptcha::can_use_captcha(), $recaptcha_url, 'registration_block' ) ) {
$captcha_result = Recaptcha::verify_captcha();
if ( \is_wp_error( $captcha_result ) ) {
return send_form_response( $captcha_result );
Expand Down
Loading
Loading