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
1 change: 1 addition & 0 deletions includes/class-newspack.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ private function includes() {
include_once NEWSPACK_ABSPATH . 'includes/wizards/audience/class-audience-content-gates.php';
include_once NEWSPACK_ABSPATH . 'includes/wizards/audience/class-audience-donations.php';
include_once NEWSPACK_ABSPATH . 'includes/wizards/audience/class-audience-subscriptions.php';
include_once NEWSPACK_ABSPATH . 'includes/wizards/audience/class-audience-integrations.php';

// Network Wizard.
include_once NEWSPACK_ABSPATH . 'includes/wizards/class-network-wizard.php';
Expand Down
1 change: 1 addition & 0 deletions includes/class-wizards.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public static function init_wizards() {
'audience-campaigns' => new Audience_Campaigns(),
'audience-content-gates' => new Audience_Content_Gates(),
'audience-donations' => new Audience_Donations(),
'audience-integrations' => new Audience_Integrations(),
'listings' => new Listings_Wizard(),
'network' => new Network_Wizard(),
'newsletters' => new Newsletters_Wizard(),
Expand Down
41 changes: 41 additions & 0 deletions includes/reader-activation/class-integrations.php
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,47 @@ public static function are_integrations_registered() {
return self::$integrations_registered;
}

/**
* Get settings config for all integrations that have settings fields.
*
* @return array Keyed array of integration settings.
*/
public static function get_all_integration_settings() {
$result = [];
foreach ( self::$integrations as $id => $integration ) {
$fields = $integration->get_settings_fields();
if ( empty( $fields ) ) {
continue;
}
$result[ $id ] = [
'id' => $id,
'name' => $integration->get_name(),
'description' => $integration->get_description(),
'enabled' => self::is_enabled( $id ),
'settings' => $integration->get_settings_config(),
];
}
return $result;
}

/**
* Update settings for a specific integration.
*
* @param string $integration_id The integration ID.
* @param array $settings Key-value pairs of settings to update.
* @return bool|null True if updated, null if integration not found.
*/
public static function update_integration_settings( $integration_id, $settings ) {
$integration = self::get_integration( $integration_id );
if ( ! $integration ) {
return null;
}
foreach ( $settings as $key => $value ) {
$integration->update_settings_field_value( $key, $value );
}
return true;
}

/**
* Register a data event handler for an integration.
*
Expand Down
57 changes: 57 additions & 0 deletions includes/reader-activation/class-reader-activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,13 @@ public static function get_setting( $name ) {
if ( ! isset( $config[ $name ] ) ) {
return null;
}

// Route ESP settings to the integration.
$esp_setting = self::get_esp_integration_setting( $name );
if ( null !== $esp_setting ) {
return apply_filters( 'newspack_reader_activation_setting', $esp_setting, $name );
}

$value = \get_option( self::OPTIONS_PREFIX . $name, $config[ $name ] );

// Use default value type for casting bool option value.
Expand All @@ -440,6 +447,38 @@ public static function get_setting( $name ) {
return apply_filters( 'newspack_reader_activation_setting', $value, $name );
}

/**
* Get an ESP setting value from the integration instance.
*
* @param string $name Setting name.
* @return mixed|null The setting value, or null if this setting is not an ESP integration setting.
*/
private static function get_esp_integration_setting( $name ) {
static $esp_keys = [
'mailchimp_audience_id',
'mailchimp_reader_default_status',
'active_campaign_master_list',
'constant_contact_list_id',
'sync_esp_delete',
'sync_esp',
];

if ( ! in_array( $name, $esp_keys, true ) ) {
return null;
}

$esp = Reader_Activation\Integrations::get_integration( 'esp' );
if ( ! $esp ) {
return null;
}

if ( 'sync_esp' === $name ) {
return $esp->is_enabled();
}

return $esp->get_settings_field_value( $name );
}

/**
* Update a setting value.
*
Expand Down Expand Up @@ -472,6 +511,24 @@ public static function update_setting( $key, $value ) {
return Sync\Metadata::update_fields( $value );
}

// Route sync_esp to the integration enabled state.
if ( 'sync_esp' === $key ) {
if ( $value ) {
Reader_Activation\Integrations::enable( 'esp' );
} else {
Reader_Activation\Integrations::disable( 'esp' );
}
// Also write to legacy option for backward compat with external hooks.
\update_option( self::OPTIONS_PREFIX . $key, $value );
return true;
}

// Route ESP settings to the integration.
$esp = Reader_Activation\Integrations::get_integration( 'esp' );
if ( $esp && null !== self::get_esp_integration_setting( $key ) ) {
return $esp->update_settings_field_value( $key, $value );
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private static function pull_sync( $user_id, $integrations ) {
$failed = [];

foreach ( $integrations as $id => $integration ) {
$selected_fields = $integration->get_selected_fields();
$selected_fields = $integration->get_enabled_incoming_fields();
if ( empty( $selected_fields ) ) {
continue;
}
Expand Down Expand Up @@ -235,9 +235,9 @@ public static function handle_ajax_pull() {
* @return true|\WP_Error True on success, WP_Error on failure.
*/
public static function pull_single_integration( $user_id, $integration ) {
$selected_fields = $integration->get_selected_fields();
$selected_fields = $integration->get_enabled_incoming_fields();
if ( empty( $selected_fields ) ) {
return new \WP_Error( 'no_selected_fields', 'No selected fields for ' . $integration->get_id() );
return new \WP_Error( 'no_selected_incoming_fields', 'No selected incoming fields for ' . $integration->get_id() );
}

try {
Expand Down Expand Up @@ -275,7 +275,7 @@ private static function schedule_async_pulls( $user_id, $integrations ) {
}

foreach ( $integrations as $integration ) {
$selected_fields = $integration->get_selected_fields();
$selected_fields = $integration->get_enabled_incoming_fields();
if ( empty( $selected_fields ) ) {
continue;
}
Expand Down
172 changes: 163 additions & 9 deletions includes/reader-activation/integrations/class-esp.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

use Newspack\Reader_Activation\Integration;
use Newspack\Reader_Activation\Sync;
use Newspack\Reader_Activation\Sync\Metadata;
use Newspack\Reader_Activation\Integrations;
use Newspack\Reader_Activation;
use Newspack_Newsletters_Contacts;
use Newspack_Newsletters_Subscription;
Expand All @@ -25,7 +27,162 @@ class ESP extends Integration {
* Constructor.
*/
public function __construct() {
parent::__construct( 'esp', __( 'ESPs Integration', 'newspack-plugin' ) );
parent::__construct(
'esp',
__( 'ESP', 'newspack-plugin' ),
__( 'Sync reader data and activity to the connected email service provider.', 'newspack-plugin' )
);
}

/**
* Register the settings fields declared by this integration.
*
* Dynamically builds the field list based on the active ESP provider.
* Only returns fields when ESP is configured.
*
* @return array Array of settings field declarations.
*/
public function register_settings_fields() {
$fields = [];
if ( ! Reader_Activation::is_esp_configured() ) {
return $fields;
}
$list_options = $this->get_list_options();
$provider = $this->get_provider();
if ( $provider ) {
switch ( $provider->service ) {
case 'mailchimp':
$fields[] = [
'key' => 'mailchimp_audience_id',
'type' => 'select',
'label' => __( 'Mailchimp Audience', 'newspack-plugin' ),
'description' => __( 'Choose an audience to receive reader activity data.', 'newspack-plugin' ),
'options' => $list_options,
'default' => '',
];
$fields[] = [
'key' => 'mailchimp_reader_default_status',
'type' => 'select',
'label' => __( 'Default reader status', 'newspack-plugin' ),
'description' => __( 'Choose which Mailchimp status readers should have by default if they are not subscribed to any newsletters.', 'newspack-plugin' ),
'options' => [
[
'label' => __( 'Transactional/Non-Subscribed', 'newspack-plugin' ),
'value' => 'transactional',
],
[
'label' => __( 'Subscribed', 'newspack-plugin' ),
'value' => 'subscribed',
],
],
'default' => 'transactional',
];
break;
case 'active_campaign':
$fields[] = [
'key' => 'active_campaign_master_list',
'type' => 'select',
'label' => __( 'ActiveCampaign Master List', 'newspack-plugin' ),
'description' => __( 'Choose a master list to which all registered readers will be added.', 'newspack-plugin' ),
'options' => $list_options,
'default' => '',
];
break;
case 'constant_contact':
$fields[] = [
'key' => 'constant_contact_list_id',
'type' => 'select',
'label' => __( 'Constant Contact Master List', 'newspack-plugin' ),
'description' => __( 'Choose a master list to which all registered readers will be added.', 'newspack-plugin' ),
'options' => $list_options,
'default' => '',
];
break;
}
}
$fields[] = [
'key' => 'sync_esp_delete',
'type' => 'checkbox',
'label' => __( 'Sync user account deletion', 'newspack-plugin' ),
'description' => __( 'When a reader account is deleted, also remove the contact from the ESP.', 'newspack-plugin' ),
'default' => true,
];
return $fields;
}

/**
* Get the active ESP provider name.
*
* @return Newspack_Newsletters_Service_Provider|null The service provider object or null if not available.
*/
private function get_provider() {
if ( class_exists( 'Newspack_Newsletters' ) ) {
return \Newspack_Newsletters::get_service_provider();
}
return null;
}

/**
* Get list options from the Newsletters API for select fields.
*
* @return array Array of options with label and value keys.
*/
private function get_list_options() {
if ( ! method_exists( 'Newspack_Newsletters_Subscription', 'get_lists' ) ) {
return [];
}

$lists = Newspack_Newsletters_Subscription::get_lists();
if ( is_wp_error( $lists ) || ! is_array( $lists ) ) {
return [];
}

$provider = $this->get_provider();

// For Mailchimp, filter out groups and tags, only include remote lists.
if ( 'mailchimp' === $provider->service ) {
$lists = $provider->get_lists( true );
}

$options = [
[
'label' => __( 'None', 'newspack-plugin' ),
'value' => '',
],
];
foreach ( $lists as $list ) {
$options[] = [
'label' => $list['name'] ?? $list['id'],
'value' => $list['id'],
];
}

return $options;
}

/**
* Get the master list ID from integration settings.
*
* @return string|false The master list ID or false.
*/
public function get_master_list_id() {
$provider = $this->get_provider();
if ( ! $provider ) {
return false;
}
switch ( $provider->service ) {
case 'mailchimp':
$audience_id = $this->get_settings_field_value( 'mailchimp_audience_id' );
return ! empty( $audience_id ) ? $audience_id : false;
case 'active_campaign':
$list_id = $this->get_settings_field_value( 'active_campaign_master_list' );
return ! empty( $list_id ) ? $list_id : false;
case 'constant_contact':
$list_id = $this->get_settings_field_value( 'constant_contact_list_id' );
return ! empty( $list_id ) ? $list_id : false;
default:
return false;
}
}

/**
Expand Down Expand Up @@ -84,14 +241,13 @@ public function can_sync( $return_errors = false ) {
);
}

if ( ! Reader_Activation::get_setting( 'sync_esp' ) ) {
if ( ! Integrations::is_enabled( $this->get_id() ) ) {
$errors->add(
'ras_esp_sync_not_enabled',
__( 'ESP sync is not enabled.', 'newspack-plugin' )
);
}

if ( ! Reader_Activation::get_esp_master_list_id() ) {
if ( ! $this->get_master_list_id() ) {
$errors->add(
'ras_esp_master_list_id_not_found',
__( 'ESP master list ID is not set.', 'newspack-plugin' )
Expand Down Expand Up @@ -119,13 +275,12 @@ public function can_sync( $return_errors = false ) {
* @return true|\WP_Error True on success or WP_Error on failure.
*/
public function push_contact_data( $contact, $context = '', $existing_contact = null ) {

$can_sync = $this->can_sync( true );
if ( $can_sync->has_errors() ) {
return $can_sync;
}

$master_list_id = Reader_Activation::get_esp_master_list_id();
$master_list_id = $this->get_master_list_id();

return Newspack_Newsletters_Contacts::upsert( $contact, $master_list_id, $context, $existing_contact );
}
Expand Down Expand Up @@ -181,16 +336,15 @@ public function test_connection() {
*
* @return Incoming_Contact_Field[]|\WP_Error Array of incoming contact field objects or WP_Error on failure.
*/
public function get_incoming_available_contact_fields() {

public function get_available_incoming_contact_fields() {
if ( ! class_exists( 'Newspack_Newsletters_Contacts' ) ) {
return new \WP_Error(
'newspack_newsletters_contacts_not_found',
__( 'Newspack Newsletters is not available.', 'newspack-plugin' )
);
}

$master_list_id = Reader_Activation::get_esp_master_list_id();
$master_list_id = $this->get_master_list_id();

if ( empty( $master_list_id ) ) {
return new \WP_Error(
Expand Down
Loading
Loading