Current File : /var/www/pediatribu/wp-content/plugins/wpforms-lite/src/Integrations/ConstantContact/V3/Api/Api.php |
<?php
namespace WPForms\Integrations\ConstantContact\V3\Api;
use RuntimeException;
use InvalidArgumentException;
use WPForms\Integrations\ConstantContact\V3\Core;
use WPForms\Integrations\ConstantContact\V3\ConstantContact;
use WPForms\Integrations\ConstantContact\V3\Api\Http\Request;
/**
* Class Api.
*
* @since 1.9.3
*/
class Api {
/**
* Account data.
*
* @since 1.9.3
*
* @var array
*/
private $account;
/**
* Request object.
*
* @since 1.9.3
*
* @var Request
*/
private $request;
/**
* API Constructor.
*
* @since 1.9.3
*
* @param array $account Account data.
*
* @throws InvalidArgumentException If arguments are invalid.
*/
public function __construct( array $account ) {
if ( empty( $account['access_token'] ) ) {
throw new InvalidArgumentException( 'Access token cannot be empty.' );
}
if ( empty( $account['refresh_token'] ) ) {
throw new InvalidArgumentException( 'Refresh token cannot be empty.' );
}
if ( empty( $account['id'] ) ) {
throw new InvalidArgumentException( 'Account ID cannot be empty.' );
}
$this->account = $account;
$this->refresh_access_token();
$this->request = new Request( $this->account['access_token'] );
}
/**
* Get custom fields in a specific format based on provided arguments.
*
* @since 1.9.3
*
* @param string|null $field The field to extract from each custom field. If null, returns all custom fields.
* @param string|null $index_key The key to index the returned array by. If null, returns a numerically indexed array.
*
* @return array
*/
public function get_custom_fields( ?string $field = null, ?string $index_key = null ): array {
$custom_fields = $this->request->get( 'v3/contact_custom_fields', [ 'limit' => 100 ] );
$custom_fields = $custom_fields->get_body();
if ( empty( $custom_fields['custom_fields'] ) || ! is_array( $custom_fields['custom_fields'] ) ) {
return [];
}
$custom_fields = $custom_fields['custom_fields'];
if ( is_null( $field ) ) {
return $custom_fields;
}
// Return plucked fields based on provided arguments.
return wp_list_pluck( $custom_fields, $field, $index_key );
}
/**
* Register a custom field.
*
* @since 1.9.3
*
* @param string $name Name of the custom field.
*
* @return string
*/
public function register_custom_field( string $name ): string {
$body = [
'label' => $name,
'type' => 'string',
];
$response = $this->request->post( 'v3/contact_custom_fields', $body );
return $response->get_body()['custom_field_id'] ?? '';
}
/**
* Get account summary.
*
* @since 1.9.3
*
* @return array
*
* @throws RuntimeException A request was failed.
*/
public function get_account_summary(): array {
$response = $this->request->get( 'v3/account/summary' );
if ( $response->has_errors() ) {
throw new RuntimeException( esc_html( $response::get_error_message() ) );
}
return $response->get_body();
}
/**
* Search contact.
*
* @since 1.9.3
*
* @param array $contact_data Contact data.
*
* @return array Contact data array.
*
* @throws RuntimeException A request was failed.
*/
private function search_contact( array $contact_data ): array {
$this->validate_contact_email( $contact_data );
$args = [
'limit' => 1,
'email' => $contact_data['email_address'],
];
$response = $this->request->get( 'v3/contacts', $args );
$body = $response->get_body();
if ( empty( $body['contacts'][0] ) || ! is_array( $body['contacts'][0] ) ) {
throw new RuntimeException( 'Contact not found.' );
}
return $body['contacts'][0];
}
/**
* Create or update contact.
*
* @since 1.9.3
*
* @param array $contact_data Contact data.
*
* @return array
*
* @throws RuntimeException A request was failed.
*/
public function subscribe_contact( array $contact_data ): array {
$this->validate_subscribe_contact( $contact_data );
$response = $this->request->post( 'v3/contacts/sign_up_form', $contact_data );
$body = $response->get_body();
if ( $response->has_errors() ) {
throw new RuntimeException( esc_html( $response::get_error_message() ) );
}
if ( empty( $body['contact_id'] ) || empty( $body['action'] ) ) {
throw new RuntimeException( 'Account was not created.' );
}
return $body;
}
/**
* Validate fields for subscribing action.
*
* @since 1.9.3
*
* @param array $contact_data Contact data.
*
* @throws InvalidArgumentException If the email address is empty.
*/
private function validate_subscribe_contact( array $contact_data ) {
$this->validate_contact_email( $contact_data );
foreach ( [ 'first_name', 'last_name', 'job_title', 'company_name', 'phone_number' ] as $key ) {
if ( isset( $contact_data[ $key ] ) && ! is_string( $contact_data[ $key ] ) ) {
throw new InvalidArgumentException( sprintf( 'The "%s" argument should be a string.', esc_html( $key ) ) );
}
}
if ( isset( $contact_data['street_address'] ) ) {
foreach ( (array) $contact_data['street_address'] as $key => $value ) {
if ( ! is_string( $value ) ) {
throw new InvalidArgumentException( sprintf( 'The "%s" argument should be a string.', esc_html( $key ) ) );
}
}
}
}
/**
* Validate contact email.
*
* @since 1.9.3
*
* @param array $contact_data Contact data.
*
* @throws InvalidArgumentException If the email address is empty.
*/
private function validate_contact_email( array $contact_data ) {
if ( empty( $contact_data['email_address'] ) || ! is_email( $contact_data['email_address'] ) ) {
throw new InvalidArgumentException( 'Email address is required.' );
}
}
/**
* Delete contact.
*
* @since 1.9.3
*
* @param array $contact_data Contact data.
*
* @return array Array with contact ID and action, empty array if no contact was found.
*
* @throws RuntimeException A request was failed.
*/
public function delete_contact( array $contact_data ): array {
$contact = $this->search_contact( $contact_data );
if ( empty( $contact['contact_id'] ) ) {
throw new RuntimeException( 'Contact not found.' );
}
$endpoint = 'v3/contacts/' . $contact['contact_id'];
$response = $this->request->delete( $endpoint );
if ( $response->has_errors() ) {
throw new RuntimeException( esc_html( $response::get_error_message() ) );
}
return [
'contact_id' => $contact['contact_id'],
'action' => 'delete',
'response' => $response->get_body(),
];
}
/**
* Unsubscribe contact.
*
* @since 1.9.3
*
* @param array $contact_data Contact data.
*
* @return array
*
* @throws InvalidArgumentException If some arguments are used wrong.
* @throws RuntimeException A request was failed.
*/
public function unsubscribe_contact( array $contact_data ): array {
if ( isset( $contact_data['opt_out_reason'] ) && ! is_string( $contact_data['opt_out_reason'] ) ) {
throw new InvalidArgumentException( sprintf( 'The "%s" argument should be a string.', 'opt_out_reason' ) );
}
$contact = $this->search_contact( $contact_data );
if ( empty( $contact['contact_id'] ) ) {
throw new RuntimeException( 'Contact not found.' );
}
$request_data = wp_parse_args(
$contact,
[
'first_name' => '',
'last_name' => '',
'company_name' => '',
'job_title' => '',
'street_address' => '',
]
);
$request_data['email_address'] = [
'address' => $contact['email_address']['address'] ?? '',
'permission_to_send' => 'unsubscribed',
'opt_out_reason' => $contact_data['opt_out_reason'] ?? '',
];
$request_data['update_source'] = 'Contact';
$response = $this->request->put( "v3/contacts/{$contact['contact_id']}", $request_data );
if ( $response->has_errors() ) {
throw new RuntimeException( esc_html( $response::get_error_message() ) );
}
return [
'contact_id' => $contact['contact_id'],
'action' => 'unsubscribe',
'response' => $response->get_body(),
];
}
/**
* Check if the access token is expired.
*
* @since 1.9.3
*
* @return bool
*/
private function is_expired_token(): bool {
$expires_in = $this->account['expires_in'] ?? 0;
/**
* Adding one minute to cover a very rare case when a few seconds are left,
* and the site runs multiple API requests.
* The last one could be outdated.
*/
return ( time() + MINUTE_IN_SECONDS ) > $expires_in;
}
/**
* Refresh access token.
*
* @since 1.9.3
*
* @throws InvalidArgumentException If the token cannot be refreshed.
*/
private function refresh_access_token() {
if ( ! $this->is_expired_token() ) {
return;
}
$response = wp_safe_remote_get(
add_query_arg(
[
'api-version' => 'v3',
'refresh_token' => $this->account['refresh_token'],
],
ConstantContact::get_middleware_url()
)
);
$response_body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $response_body['access_token'] ) ) {
throw new InvalidArgumentException( esc_html__( 'Cannot refresh the token.', 'wpforms-lite' ) );
}
$this->account = array_merge(
$this->account,
[
'access_token' => $response_body['access_token'],
'refresh_token' => $response_body['refresh_token'] ?? '',
'expires_in' => time() + (int) ( $response_body['expires_in'] ?? 0 ),
]
);
wpforms_update_providers_options( Core::SLUG, $this->account, $this->account['id'] );
}
/**
* Get a contact list.
*
* @since 1.9.3
*
* @return array
*/
public function get_contact_list(): array {
$response = $this->request->get( 'v3/contact_lists', [ 'limit' => 1000 ] );
$body = $response->get_body();
$lists = $body['lists'] ?? [];
// Replace in lists key list_id with id.
return array_map(
static function ( $contact_list ) {
return [
'id' => $contact_list['list_id'] ?? '',
'label' => $contact_list['name'] ?? '',
];
},
$lists
);
}
/**
* Get list ids in v2 to v3 format.
*
* @since 1.9.3
*
* @param array $lists List received from Constant Contact v2.
*
* @return array
*/
public function get_contact_list_xrefs( array $lists ): array {
$ids = implode( ',', wp_list_pluck( $lists, 'id' ) );
$response = $this->request->get(
'v3/contact_lists/list_id_xrefs',
[
'sequence_ids' => $ids,
'limit' => 1000,
]
);
$body = $response->get_body();
$lists = $body['xrefs'] ?? [];
return wp_list_pluck( $lists, 'list_id', 'sequence_id' );
}
}