Skip to content

Commit 79bf9a7

Browse files
authored
feat: integrations barebones (#4433)
* feat: integrations barebones * chore: reintroduce existing_contacts param * test: fix tests
1 parent 55b93e8 commit 79bf9a7

7 files changed

Lines changed: 477 additions & 1 deletion

File tree

includes/class-newspack.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ private function includes() {
9292
include_once NEWSPACK_ABSPATH . 'includes/reader-activation/sync/class-woocommerce.php';
9393
include_once NEWSPACK_ABSPATH . 'includes/reader-activation/sync/class-esp-sync.php';
9494
include_once NEWSPACK_ABSPATH . 'includes/reader-activation/sync/class-esp-sync-admin.php';
95+
include_once NEWSPACK_ABSPATH . 'includes/reader-activation/class-integrations.php';
96+
\Newspack\Reader_Activation\Integrations::init();
9597
include_once NEWSPACK_ABSPATH . 'includes/data-events/class-utils.php';
9698
include_once NEWSPACK_ABSPATH . 'includes/data-events/class-data-events.php';
9799
include_once NEWSPACK_ABSPATH . 'includes/data-events/class-webhooks.php';
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<?php
2+
/**
3+
* Integrations management class
4+
*
5+
* @package Newspack
6+
*/
7+
8+
namespace Newspack\Reader_Activation;
9+
10+
defined( 'ABSPATH' ) || exit;
11+
12+
/**
13+
* Integrations Management Class.
14+
*
15+
* Manages registration, enabling/disabling, and retrieval of integrations.
16+
*/
17+
class Integrations {
18+
/**
19+
* Registered integrations.
20+
*
21+
* @var Integration[]
22+
*/
23+
private static $integrations = [];
24+
25+
/**
26+
* Option name for storing enabled integrations.
27+
*
28+
* @var string
29+
*/
30+
const OPTION_NAME = 'newspack_reader_activation_enabled_integrations';
31+
32+
/**
33+
* Initialize integrations system.
34+
*/
35+
public static function init() {
36+
// Include required files.
37+
require_once __DIR__ . '/integrations/class-integration.php';
38+
39+
self::register_native_integrations();
40+
41+
// Hook for other plugins/code to register their integrations.
42+
do_action( 'newspack_reader_activation_register_integrations' );
43+
}
44+
45+
/**
46+
* Register native integrations.
47+
*/
48+
private static function register_native_integrations() {
49+
self::register( new Integrations\ESP() );
50+
51+
// hardcode ESP integration as enabled for now.
52+
self::enable( 'esp' );
53+
}
54+
55+
/**
56+
* Register a new integration.
57+
*
58+
* @param Integration $integration The integration instance to register.
59+
*
60+
* @return bool True if registered successfully, false if already registered.
61+
*/
62+
public static function register( $integration ) {
63+
if ( ! $integration instanceof Integration ) {
64+
return false;
65+
}
66+
67+
$id = $integration->get_id();
68+
69+
if ( isset( self::$integrations[ $id ] ) ) {
70+
return false;
71+
}
72+
73+
self::$integrations[ $id ] = $integration;
74+
75+
return true;
76+
}
77+
78+
/**
79+
* Enable an integration.
80+
*
81+
* @param string $integration_id The integration ID to enable.
82+
*
83+
* @return bool True if enabled successfully, false otherwise.
84+
*/
85+
public static function enable( $integration_id ) {
86+
if ( ! isset( self::$integrations[ $integration_id ] ) ) {
87+
return false;
88+
}
89+
90+
$enabled = self::get_enabled_integration_ids();
91+
92+
if ( in_array( $integration_id, $enabled, true ) ) {
93+
return true;
94+
}
95+
96+
$enabled[] = $integration_id;
97+
98+
return update_option( self::OPTION_NAME, $enabled );
99+
}
100+
101+
/**
102+
* Disable an integration.
103+
*
104+
* @param string $integration_id The integration ID to disable.
105+
*
106+
* @return bool True if disabled successfully, false otherwise.
107+
*/
108+
public static function disable( $integration_id ) {
109+
$enabled = self::get_enabled_integration_ids();
110+
111+
$key = array_search( $integration_id, $enabled, true );
112+
113+
if ( false === $key ) {
114+
return true;
115+
}
116+
117+
unset( $enabled[ $key ] );
118+
119+
return update_option( self::OPTION_NAME, array_values( $enabled ) );
120+
}
121+
122+
/**
123+
* Get all available integrations.
124+
*
125+
* @return Integration[] Array of all registered integration instances.
126+
*/
127+
public static function get_available_integrations() {
128+
return self::$integrations;
129+
}
130+
131+
/**
132+
* Get active integrations.
133+
*
134+
* @return Integration[] Array of enabled integration instances.
135+
*/
136+
public static function get_active_integrations() {
137+
$enabled_ids = self::get_enabled_integration_ids();
138+
$active = [];
139+
140+
foreach ( $enabled_ids as $id ) {
141+
if ( isset( self::$integrations[ $id ] ) ) {
142+
$active[ $id ] = self::$integrations[ $id ];
143+
}
144+
}
145+
146+
return $active;
147+
}
148+
149+
/**
150+
* Get a specific integration by ID.
151+
*
152+
* @param string $integration_id The integration ID.
153+
*
154+
* @return Integration|null The integration instance or null if not found.
155+
*/
156+
public static function get_integration( $integration_id ) {
157+
return self::$integrations[ $integration_id ] ?? null;
158+
}
159+
160+
/**
161+
* Check if an integration is enabled.
162+
*
163+
* @param string $integration_id The integration ID.
164+
*
165+
* @return bool True if enabled, false otherwise.
166+
*/
167+
public static function is_enabled( $integration_id ) {
168+
$enabled_ids = self::get_enabled_integration_ids();
169+
return in_array( $integration_id, $enabled_ids, true );
170+
}
171+
172+
/**
173+
* Get enabled integration IDs from option.
174+
*
175+
* @return array Array of enabled integration IDs.
176+
*/
177+
private static function get_enabled_integration_ids() {
178+
$enabled = get_option( self::OPTION_NAME, [] );
179+
180+
if ( ! is_array( $enabled ) ) {
181+
return [];
182+
}
183+
184+
return $enabled;
185+
}
186+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/**
3+
* ESP integration
4+
*
5+
* @package Newspack
6+
*/
7+
8+
namespace Newspack\Reader_Activation\Integrations;
9+
10+
use Newspack\Reader_Activation\Integration;
11+
use Newspack\Reader_Activation;
12+
use Newspack_Newsletters_Contacts;
13+
14+
defined( 'ABSPATH' ) || exit;
15+
16+
/**
17+
* ESP Integration Class.
18+
*
19+
* Generic integration for ESPs using Newspack Newsletters plugin.
20+
*/
21+
class ESP extends Integration {
22+
/**
23+
* Constructor.
24+
*/
25+
public function __construct() {
26+
parent::__construct( 'esp', __( 'ESPs Integration', 'newspack-plugin' ) );
27+
}
28+
29+
/**
30+
* Push contact data to the integration destination.
31+
*
32+
* @param array $contact The contact data to push.
33+
* @param string $context Optional. The context of the sync.
34+
* @param array|null $existing_contact Optional. Existing contact data if available.
35+
*
36+
* @return true|\WP_Error True on success or WP_Error on failure.
37+
*/
38+
public function push_contact_data( $contact, $context = '', $existing_contact = null ) {
39+
40+
// For now, the can_esp_sync method is the gatekeeper for ESP syncs. We'll move that logic here later.
41+
42+
$master_list_id = Reader_Activation::get_esp_master_list_id();
43+
44+
return Newspack_Newsletters_Contacts::upsert( $contact, $master_list_id, $context, $existing_contact );
45+
}
46+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* Base integration class for contact data syncing.
4+
*
5+
* @package Newspack
6+
*/
7+
8+
namespace Newspack\Reader_Activation;
9+
10+
defined( 'ABSPATH' ) || exit;
11+
12+
/**
13+
* Base Integration Class.
14+
*
15+
* This class should be extended by specific integration implementations.
16+
*/
17+
abstract class Integration {
18+
/**
19+
* The unique identifier for this integration.
20+
*
21+
* @var string
22+
*/
23+
protected $id;
24+
25+
/**
26+
* The display name for this integration.
27+
*
28+
* @var string
29+
*/
30+
protected $name;
31+
32+
/**
33+
* Settings fields for this integration.
34+
*
35+
* @var array
36+
*/
37+
protected $settings_fields = [];
38+
39+
/**
40+
* Constructor.
41+
*
42+
* @param string $id The unique identifier for this integration.
43+
* @param string $name The display name for this integration.
44+
*/
45+
public function __construct( $id, $name ) {
46+
$this->id = $id;
47+
$this->name = $name;
48+
}
49+
50+
/**
51+
* Get the integration ID.
52+
*
53+
* @return string The integration ID.
54+
*/
55+
public function get_id() {
56+
return $this->id;
57+
}
58+
59+
/**
60+
* Get the integration name.
61+
*
62+
* @return string The integration name.
63+
*/
64+
public function get_name() {
65+
return $this->name;
66+
}
67+
68+
69+
/**
70+
* Push contact data to the integration destination.
71+
*
72+
* This method should be implemented by child classes to send
73+
* contact data to their specific integration destination.
74+
*
75+
* @param array $contact The contact data to push.
76+
* @param string $context Optional. The context of the sync.
77+
* @param array|null $existing_contact Optional. Existing contact data if available.
78+
*
79+
* @return true|\WP_Error True on success or WP_Error on failure.
80+
*/
81+
abstract public function push_contact_data( $contact, $context = '', $existing_contact = null );
82+
}

includes/reader-activation/sync/class-esp-sync.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Newspack\Reader_Activation;
99

1010
use Newspack\Reader_Activation;
11+
use Newspack\Reader_Activation\Integrations;
1112
use Newspack\Data_Events;
1213
use Newspack\Logger;
1314

@@ -135,7 +136,14 @@ public static function sync( $contact, $context = '', $existing_contact = null )
135136
*/
136137
$contact = \apply_filters( 'newspack_esp_sync_contact', $contact, $context );
137138
$contact = Sync\Metadata::normalize_contact_data( $contact );
138-
$result = \Newspack_Newsletters_Contacts::upsert( $contact, $master_list_id, $context, $existing_contact );
139+
140+
$integrations = Integrations::get_active_integrations();
141+
142+
foreach ( $integrations as $integration ) {
143+
// TODO: We know there's only one integration for now and we expect result to be wp_error or true. We'll refactor this to do a try catch.
144+
// Not changing it now because of the retry scheduled by Newspack\WooCommerce_My_Account::sync_email_change_with_esp.
145+
$result = $integration->push_contact_data( $contact, $context, $existing_contact );
146+
}
139147

140148
return \is_wp_error( $result ) ? $result : true;
141149
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Mock Integration.
4+
*
5+
* @package Newspack\Tests\Unit\Integrations
6+
*/
7+
8+
use Newspack\Reader_Activation\Integration;
9+
10+
/**
11+
* Concrete test implementation of Integration.
12+
*/
13+
class Sample_Integration extends Integration {
14+
/**
15+
* Push contact data (test implementation).
16+
*
17+
* @param array $contact The contact data.
18+
* @param string $context The sync context.
19+
* @param array|null $existing_contact Existing contact data if available.
20+
* @return true
21+
*/
22+
public function push_contact_data( $contact, $context = '', $existing_contact = null ) {
23+
return true;
24+
}
25+
}

0 commit comments

Comments
 (0)