Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 21 additions & 0 deletions includes/reader-activation/sync/class-contact-metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
* Reader Activation Class.
*/
abstract class Contact_Metadata {
/**
* The date format to use for all date fields, which is YYYY-MM-DD HH:MM:SS.
*/
const DATE_FORMAT = 'Y-m-d H:i:s';

/**
* The WP_User object.
Expand Down Expand Up @@ -122,4 +126,21 @@ public function get_full_name() {
}
return '';
}

/**
* Format a date string to MM/DD/YYYY.
*
* @param string $date_string Date string from WooCommerce.
* @return string Formatted date or empty string.
*/
protected function format_date( $date_string ) {
if ( empty( $date_string ) || '0' === $date_string ) {
return '';
}
$timestamp = strtotime( $date_string );
if ( ! $timestamp ) {
return '';
}
return gmdate( self::DATE_FORMAT, $timestamp );
}
}
7 changes: 7 additions & 0 deletions includes/reader-activation/sync/class-contact-sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Newspack\Reader_Activation\Integrations;
use Newspack\Data_Events;
use Newspack\Logger;
use Newspack\Reader_Activation\Sync\Metadata;

defined( 'ABSPATH' ) || exit;

Expand Down Expand Up @@ -132,6 +133,12 @@ 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
243 changes: 235 additions & 8 deletions includes/reader-activation/sync/contact-metadata/class-donation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,32 @@

namespace Newspack\Reader_Activation\Sync\Contact_Metadata;

use Newspack\Reader_Activation\Sync\Contact_Metadata;
use Newspack\Donations;
use Newspack\WooCommerce_Connection;

defined( 'ABSPATH' ) || exit;

/**
* Donation metadata class.
*
* Extends Subscription to reuse subscription data access helpers,
* with the filter inverted to only consider donation subscriptions.
*/
class Donation extends Contact_Metadata {
class Donation extends Subscription {

/**
* Whether or not the metadata fields of this class are available to be synced.
* Cache for the one-time donation order.
*
* @return boolean
* @var \WC_Order|null
*/
public static function is_available() {
return true;
}
private $one_time_donation_order_cache;

/**
* Whether the one-time donation order has been resolved.
*
* @var bool
*/
private $one_time_donation_order_resolved = false;

/**
* The name of the metadata class, used as a section name for the fields handled by this class when syncing and in the UI for selecting which fields to sync.
Expand Down Expand Up @@ -62,6 +71,224 @@ public static function get_fields() {
* @return array
*/
public function get_metadata() {
return [];
if ( ! $this->user || ! function_exists( 'wcs_get_users_subscriptions' ) ) {
return [];
}

return [
'Donor_Status' => $this->get_donor_status(),
'Active_Donation_Count' => $this->get_active_subscription_count(),
'Current_Donation_Start_Date' => $this->get_current_subscription_start_date(),
'Current_Donation_End_Date' => $this->get_current_subscription_end_date(),
'Current_Donation_Cycle' => $this->get_current_subscription_billing_cycle(),
'Current_Recurring_Donation' => $this->get_current_subscription_recurring_payment(),
'Next_Donation_Date' => $this->get_current_subscription_next_payment_date(),
'Current_Donation_Product_Name' => $this->get_current_donation_product_name(),
'Previous_Donation_Product' => $this->get_previous_subscription_product(),
'Previous_Donation_Amount' => $this->get_previous_donation_amount(),
'Last_Donation_Amount' => $this->get_last_donation_amount(),
'Last_Donation_Date' => $this->get_last_donation_date(),
];
}

/**
* Whether the given subscription is relevant to this metadata class.
*
* For Donation, only donation subscriptions are relevant.
*
* @param \WC_Subscription $subscription Subscription object.
* @return bool
*/
protected function is_relevant_subscription( $subscription ) {
return Donations::is_donation_order( $subscription );
}

/**
* Get the most recent one-time donation order for the current user.
*
* @return \WC_Order|null
*/
protected function get_one_time_donation_order() {
if ( $this->one_time_donation_order_resolved ) {
return $this->one_time_donation_order_cache;
}

$this->one_time_donation_order_resolved = true;

if ( ! $this->user ) {
return null;
}

$donation_product = Donations::get_donation_product( 'once' );
if ( ! $donation_product ) {
return null;
}

$user_has_donated = \wc_customer_bought_product( null, $this->user->ID, $donation_product );
if ( ! $user_has_donated ) {
return null;
}

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

foreach ( $orders as $order ) {
if ( Donations::is_donation_order( $order ) ) {
$this->one_time_donation_order_cache = $order;
return $this->one_time_donation_order_cache;
}
}
} while ( ! empty( $orders ) );

return null;
}

/**
* Get the donor status label.
*
* Returns a summarized label: Monthly Donor, Yearly Donor, Ex-Monthly Donor, Ex-Yearly Donor, or Donor.
*
* @return string
*/
protected function get_donor_status() {
$subscription = $this->get_current_subscription();

if ( $subscription ) {
$donor_status = 'Donor';
$billing_period = $subscription->get_billing_period();

if ( 'month' === $billing_period ) {
$donor_status = 'Monthly ' . $donor_status;
} elseif ( 'year' === $billing_period ) {
$donor_status = 'Yearly ' . $donor_status;
}

if ( $subscription->has_status( WooCommerce_Connection::FORMER_SUBSCRIBER_STATUSES ) ) {
$donor_status = 'Ex-' . $donor_status;
}

return $donor_status;
}

// Fallback: check for one-time donation.
$one_time_order = $this->get_one_time_donation_order();
if ( $one_time_order ) {
return 'Donor';
}

return '';
}

/**
* Get the donation product name, with fallback to one-time donation order.
*
* @return string
*/
protected function get_current_donation_product_name() {
$name = $this->get_current_subscription_product_name();
if ( ! empty( $name ) ) {
return $name;
}

$one_time_order = $this->get_one_time_donation_order();
if ( $one_time_order ) {
$items = $one_time_order->get_items();
if ( ! empty( $items ) ) {
return reset( $items )->get_name();
}
}

return '';
}

/**
* Get the previous donation amount before a plan switch.
*
* @return string
*/
protected function get_previous_donation_amount() {
$subscription = $this->get_current_subscription();
if ( ! $subscription ) {
return '';
}

$switch_orders = $subscription->get_related_orders( 'all', 'switch' );
if ( empty( $switch_orders ) ) {
return '';
}

// Get the most recent switch order.
$switch_order = reset( $switch_orders );
if ( is_numeric( $switch_order ) ) {
$switch_order = \wc_get_order( $switch_order );
}
if ( ! $switch_order ) {
return '';
}

$switch_data = $switch_order->get_meta( '_subscription_switch_data' );
if ( ! empty( $switch_data ) && is_array( $switch_data ) ) {
$sub_switch_data = isset( $switch_data[ $subscription->get_id() ] ) ? $switch_data[ $subscription->get_id() ] : reset( $switch_data );
if ( ! empty( $sub_switch_data['old_subscription_id'] ) ) {
$old_subscription = \wcs_get_subscription( $sub_switch_data['old_subscription_id'] );
if ( $old_subscription ) {
return $old_subscription->get_total();
}
}
}

return '';
}

/**
* Get the last donation amount, with fallback to one-time donation order.
*
* @return string
*/
protected function get_last_donation_amount() {
$amount = $this->get_last_payment_amount();
if ( ! empty( $amount ) ) {
return $amount;
}

$one_time_order = $this->get_one_time_donation_order();
if ( $one_time_order ) {
return $one_time_order->get_total();
}

return '';
}

/**
* Get the last donation date, with fallback to one-time donation order.
*
* @return string
*/
protected function get_last_donation_date() {
$date = $this->get_last_payment_date();
if ( ! empty( $date ) ) {
return $date;
}

$one_time_order = $this->get_one_time_donation_order();
if ( $one_time_order ) {
$date_paid = $one_time_order->get_date_paid();
if ( ! empty( $date_paid ) ) {
return $date_paid->date( self::DATE_FORMAT );
}
}

return '';
}
}
Loading
Loading