Initial commit
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator;
|
||||
|
||||
use FluentCrm\App\Services\CrmMigrator\Api\ActiveCampaign;
|
||||
use FluentCrm\Framework\Support\Arr;
|
||||
|
||||
class ActiveCampaignMigrator extends BaseMigrator
|
||||
{
|
||||
public function getInfo()
|
||||
{
|
||||
$infoSvg = $this->getInfoIcon();
|
||||
|
||||
$logo = $this->getSvgLogo();
|
||||
|
||||
return [
|
||||
'title' => 'ActiveCampaign',
|
||||
'description' => __('Transfer your ActiveCampaign tags and contacts to FluentCRM', 'fluent-crm'),
|
||||
'logo' => fluentCrmMix('images/migrators/active_campaign.png'),
|
||||
'logo_svg' => $logo,
|
||||
'supports' => [
|
||||
'tags' => false,
|
||||
'lists' => false,
|
||||
'empty_tags' => true,
|
||||
'active_imports_only' => true,
|
||||
'has_list_mapper' => true
|
||||
],
|
||||
'credentials' => [
|
||||
'api_key' => '',
|
||||
'api_url' => ''
|
||||
],
|
||||
'field_map_info' => __('Email and main contact fields will be mapped automatically', 'fluent-crm'),
|
||||
'credential_fields' => [
|
||||
'api_url' => [
|
||||
'label' => __('API Access URL', 'fluent-crm'),
|
||||
'placeholder' => __('API Access URL', 'fluent-crm'),
|
||||
'data_type' => 'url',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find Account ID Settings -> Developer -> API Access', 'fluent-crm')
|
||||
],
|
||||
'api_key' => [
|
||||
'label' => __('API Access Key', 'fluent-crm'),
|
||||
'placeholder' => __('ActiveCampaign API Token', 'fluent-crm'),
|
||||
'data_type' => 'password',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find your API key at ActiveCampaign Settings -> Developer -> API Access', 'fluent-crm')
|
||||
]
|
||||
],
|
||||
'refresh_on_list_change' => false,
|
||||
'doc_url' => 'https://fluentcrm.com/docs/migrating-into-fluentcrm-from-activecampaign/'
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyCredentials($credential)
|
||||
{
|
||||
$api = $this->getApi($credential);
|
||||
|
||||
try {
|
||||
$result = $api->auth_test();
|
||||
if (is_wp_error($result)) {
|
||||
return $result;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
return new \WP_Error('api_error', $exception->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getListTagMappings($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$data = [];
|
||||
|
||||
$lists = $api->get_lists();
|
||||
|
||||
if (is_wp_error($lists)) {
|
||||
return $lists;
|
||||
}
|
||||
|
||||
$formattedLists = [];
|
||||
foreach ($lists as $list) {
|
||||
if (!is_array($list)) {
|
||||
continue;
|
||||
}
|
||||
$formattedLists[] = [
|
||||
'remote_id' => (string)$list['id'],
|
||||
'local_id' => '',
|
||||
'will_create' => 'no',
|
||||
'remote_name' => (string)$list['name']
|
||||
];
|
||||
}
|
||||
|
||||
$data['mapped_lists'] = $formattedLists;
|
||||
|
||||
$tags = $api->getTags();
|
||||
|
||||
if (is_wp_error($tags)) {
|
||||
return $tags;
|
||||
}
|
||||
|
||||
$formattedTags = [];
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$formattedTags[] = [
|
||||
'remote_name' => $tag['name'],
|
||||
'remote_id' => $tag['id'],
|
||||
'will_create' => 'no',
|
||||
'fluentcrm_id' => ''
|
||||
];
|
||||
}
|
||||
$data['tags'] = $formattedTags;
|
||||
|
||||
$mergeFields = $api->get_custom_fields();
|
||||
|
||||
if (is_wp_error($mergeFields)) {
|
||||
$mergeFields = [];
|
||||
}
|
||||
|
||||
$formattedContactFields = [];
|
||||
|
||||
foreach ($mergeFields as $field) {
|
||||
if (!is_array($field)) {
|
||||
continue;
|
||||
}
|
||||
$item = [
|
||||
'type' => 'any',
|
||||
'remote_label' => $field['title'],
|
||||
'remote_tag' => $field['id'],
|
||||
'fluentcrm_field' => '',
|
||||
'remote_type' => $field['type'],
|
||||
'will_skip' => 'no'
|
||||
];
|
||||
|
||||
$formattedContactFields[] = $item;
|
||||
}
|
||||
|
||||
$data['contact_fields'] = $formattedContactFields;
|
||||
$data['contact_fillables'] = $this->getFillables();
|
||||
|
||||
unset($data['contact_fillables']['first_name']);
|
||||
unset($data['contact_fillables']['last_name']);
|
||||
unset($data['contact_fillables']['full_name']);
|
||||
unset($data['contact_fillables']['phone']);
|
||||
|
||||
$data['all_ready'] = true;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSummary($postedData)
|
||||
{
|
||||
$mappedLists = Arr::get($postedData, 'mapped_lists', []);
|
||||
|
||||
$listCounts = count($mappedLists);
|
||||
|
||||
$message = __('Based on your selections ', 'fluent-crm') . $listCounts . __(' lists and associate contacts will be imported', 'fluent-crm');
|
||||
|
||||
return [
|
||||
'subscribed_count' => 1,
|
||||
'unsubscribed_count' => 0,
|
||||
'all_count' => 1,
|
||||
'message' => $message
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
public function runImport($postedData)
|
||||
{
|
||||
if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) {
|
||||
define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true);
|
||||
}
|
||||
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
// Initialize or retrieve the current offset
|
||||
$offset = Arr::get($postedData, 'completed', 0);
|
||||
|
||||
$tagMappings = Arr::get($postedData, 'tags', []);
|
||||
|
||||
$taggingArray = $this->mapTags($tagMappings);
|
||||
|
||||
$mapSettings = Arr::get($postedData, 'map_settings', []);
|
||||
|
||||
$listingArray = $this->getListingArray(Arr::get($postedData, 'mapped_lists', []));
|
||||
|
||||
$subscribedOnly = false;
|
||||
if ($mapSettings['import_active_only'] == 'yes') {
|
||||
$subscribedOnly = true;
|
||||
}
|
||||
|
||||
// Prepare the API parameters
|
||||
$params = [
|
||||
'offset' => $offset,
|
||||
'status' => $subscribedOnly ? '1' : '-1',
|
||||
];
|
||||
$subscribers = $api->getContacts($params);
|
||||
|
||||
if (is_wp_error($subscribers)) {
|
||||
if ($offset > 0) {
|
||||
return [
|
||||
'completed' => $offset,
|
||||
'total' => $offset,
|
||||
'has_more' => false,
|
||||
'hide_progress' => true
|
||||
];
|
||||
}
|
||||
|
||||
return $subscribers;
|
||||
}
|
||||
|
||||
$fieldMaps = Arr::get($postedData, 'contact_fields', []);
|
||||
$stepCompletedCount = 0;
|
||||
foreach ($subscribers as $subscriber) {
|
||||
|
||||
if (!is_array($subscriber)) {
|
||||
continue;
|
||||
}
|
||||
$stepCompletedCount++;
|
||||
|
||||
if ($subscribedOnly && $subscriber['status'] != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = 'subscribed';
|
||||
if ($subscriber['status'] != 1) {
|
||||
$status = 'pending';
|
||||
}
|
||||
|
||||
$data = [
|
||||
'email' => $subscriber['email'],
|
||||
'first_name' => $subscriber['first_name'],
|
||||
'last_name' => $subscriber['last_name'],
|
||||
'phone' => $subscriber['phone'],
|
||||
'created_at' => gmdate('Y-m-d H:i:s', strtotime($subscriber['cdate'])),
|
||||
'source' => 'ActiveCampaign',
|
||||
'ip' => $subscriber['ip'],
|
||||
'status' => $status,
|
||||
'lists' => []
|
||||
];
|
||||
|
||||
|
||||
if (!empty($subscriber['fields'])) {
|
||||
$customFields = [];
|
||||
foreach ($subscriber['fields'] as $field) {
|
||||
$val = $field['val'];
|
||||
/**
|
||||
* ActiveCampaign api return checkbox & listbox field value as ||VALUE|| format
|
||||
* For example: ||a||, ||yes||
|
||||
* Here we remove the || from the value
|
||||
*/
|
||||
if (in_array($field['type'], ['checkbox', 'listbox'])) {
|
||||
$val = array_values( array_filter( explode( '||', $val ) ) );
|
||||
}
|
||||
$customFields[$field['perstag']] = $val;
|
||||
}
|
||||
if ($customFields) {
|
||||
$mergeData = $this->getMergedData($customFields, $fieldMaps);
|
||||
|
||||
// Create values for multiselect and checkbox fields.
|
||||
$this->maybeCreateFieldOptions( $mergeData );
|
||||
|
||||
if ($mergeData) {
|
||||
$data = array_merge($data, $mergeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map Lists here
|
||||
if ($listingArray && $subscriber['lists']) {
|
||||
foreach ($subscriber['lists'] as $subList) {
|
||||
$listId = $subList['listid'];
|
||||
if (isset($listingArray[$listId])) {
|
||||
$data['lists'][] = $listingArray[$listId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($mapSettings['local_list_id'])) {
|
||||
$data['lists'][] = $mapSettings['local_list_id'];
|
||||
}
|
||||
|
||||
if (!empty($subscriber['tags']) && $taggingArray) {
|
||||
$tagIds = [];
|
||||
foreach ($subscriber['tags'] as $contactTag) {
|
||||
if (!empty($taggingArray[$contactTag])) {
|
||||
$tagIds[] = $taggingArray[$contactTag];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($tagIds) && !empty($mapSettings['local_tag_id'])) {
|
||||
$tagIds = [$mapSettings['local_tag_id']];
|
||||
}
|
||||
|
||||
$data['tags'] = $tagIds;
|
||||
|
||||
} else if (!empty($mapSettings['local_tag_id'])) {
|
||||
$data['tags'] = [$mapSettings['local_tag_id']];
|
||||
}
|
||||
|
||||
$contact = FluentCrmApi('contacts')->createOrUpdate($data);
|
||||
|
||||
if ($subscribedOnly && $contact && $contact->status != 'subscribed') {
|
||||
$contact->updateStatus('subscribed');
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'completed' => $offset + 100,
|
||||
'total' => $offset + 100,
|
||||
'has_more' => !empty($subscribers),
|
||||
'hide_progress' => true,
|
||||
'message' => ($offset + 100) . __(' contacts have been imported so far.', 'fluent-crm')
|
||||
];
|
||||
}
|
||||
|
||||
private function getApi($credentials)
|
||||
{
|
||||
return new ActiveCampaign($credentials['api_url'], $credentials['api_key']);
|
||||
}
|
||||
|
||||
private function getListingArray($listMappings)
|
||||
{
|
||||
$formattedMaps = [];
|
||||
|
||||
foreach ($listMappings as $listMapping) {
|
||||
$remoteId = $listMapping['remote_id'];
|
||||
|
||||
if ($listMapping['will_create'] == 'yes') {
|
||||
$remoteName = sanitize_text_field($listMapping['remote_name']);
|
||||
if (!$remoteName) {
|
||||
continue;
|
||||
}
|
||||
$listMapping['fluentcrm_id'] = $this->getListId($remoteName);
|
||||
}
|
||||
|
||||
if (empty($listMapping['fluentcrm_id'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$formattedMaps[$remoteId] = (int)$listMapping['fluentcrm_id'];
|
||||
}
|
||||
|
||||
return $formattedMaps;
|
||||
}
|
||||
|
||||
public function getSvgLogo()
|
||||
{
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||
<path d="M16 30C23.732 30 30 23.732 30 16C30 8.26801 23.732 2 16 2C8.26801 2 2 8.26801 2 16C2 23.732 8.26801 30 16 30Z" fill="#004CFF"/>
|
||||
<path d="M20.7497 15.9683L12.5577 21.4086C12.1782 21.6616 11.9883 22.0727 11.9883 22.4839V23.8439L21.92 17.3283C22.3628 17.0121 22.6475 16.5059 22.6475 15.9683C22.6475 15.4306 22.3945 14.9244 21.92 14.6081L11.9883 8.15576V9.42089C11.9883 9.8638 12.2097 10.2749 12.5577 10.4963L20.7497 15.9683Z" fill="white"/>
|
||||
<path d="M15.8155 16.4112C16.2583 16.6958 16.8277 16.6958 17.2705 16.4112L17.9663 15.9367L12.779 12.4259C12.4628 12.2044 11.9883 12.4259 11.9883 12.837V13.8807L14.6768 15.6837L15.8155 16.4112Z" fill="white"/>
|
||||
</svg>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator\Api;
|
||||
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class ActiveCampaign
|
||||
{
|
||||
protected $apiUrl = null;
|
||||
|
||||
protected $apiKey = null;
|
||||
|
||||
public function __construct($apiUrl, $apiKey = null)
|
||||
{
|
||||
$this->apiUrl = $apiUrl;
|
||||
$this->apiKey = $apiKey;
|
||||
}
|
||||
|
||||
public function default_options()
|
||||
{
|
||||
return array(
|
||||
'api_key' => $this->apiKey,
|
||||
'api_output' => 'json'
|
||||
);
|
||||
}
|
||||
|
||||
public function make_request($action, $options = array(), $method = 'GET')
|
||||
{
|
||||
/* Build request options string. */
|
||||
$request_options = $this->default_options();
|
||||
$request_options['api_action'] = $action;
|
||||
|
||||
if ($request_options['api_action'] == 'contact_edit')
|
||||
$request_options['overwrite'] = '0';
|
||||
|
||||
$request_options = http_build_query($request_options);
|
||||
$request_options .= ($method == 'GET') ? '&' . http_build_query($options) : null;
|
||||
|
||||
/* Build request URL. */
|
||||
$request_url = untrailingslashit($this->apiUrl) . '/admin/api.php?' . $request_options;
|
||||
$response = null;
|
||||
/* Execute request based on method. */
|
||||
switch ($method) {
|
||||
|
||||
case 'POST':
|
||||
$args = array(
|
||||
'body' => $options,
|
||||
'timeout' => 30
|
||||
);
|
||||
$response = wp_remote_post($request_url, $args);
|
||||
break;
|
||||
|
||||
case 'GET':
|
||||
$response = wp_remote_get($request_url, [
|
||||
'timeout' => 30
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
$error = $this->maybeError($response);
|
||||
if ($error) {
|
||||
return $error;
|
||||
}
|
||||
return json_decode($response['body'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the provided API credentials.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function auth_test()
|
||||
{
|
||||
/* Build options string. */
|
||||
$request_options = $this->default_options();
|
||||
$request_options['api_action'] = 'list_paginator';
|
||||
$request_options = http_build_query($request_options);
|
||||
|
||||
/* Setup request URL. */
|
||||
$request_url = untrailingslashit($this->apiUrl) . '/admin/api.php?' . $request_options;
|
||||
|
||||
/* Execute request. */
|
||||
$response = wp_remote_get($request_url);
|
||||
|
||||
$error = $this->maybeError($response);
|
||||
if ($error) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all custom list fields.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function get_custom_fields()
|
||||
{
|
||||
return $this->make_request('list_field_view', array('ids' => 'all'));
|
||||
}
|
||||
|
||||
public function getContacts($args = ['offset' => 0, 'status' => -1])
|
||||
{
|
||||
|
||||
$args = wp_parse_args(
|
||||
$args,
|
||||
array(
|
||||
'offset' => 0,
|
||||
'status' => $args['status'],
|
||||
'include' => 'contactTags,contactLists,fieldValues',
|
||||
'limit' => 100,
|
||||
'api_key' => $this->apiKey,
|
||||
)
|
||||
);
|
||||
|
||||
$subscribers = array();
|
||||
|
||||
$request = add_query_arg( $args, untrailingslashit( $this->apiUrl ) . '/api/3/contacts' );
|
||||
$response = wp_safe_remote_get( $request );
|
||||
|
||||
$error = $this->maybeError( $response );
|
||||
if ( $error ) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$response = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
|
||||
if ( ! empty( $response->contacts ) ) {
|
||||
|
||||
// Base subscriber data.
|
||||
|
||||
foreach ( $response->contacts as $contact ) {
|
||||
|
||||
$subscribers[ $contact->id ] = array(
|
||||
'first_name' => $contact->{'firstName'},
|
||||
'last_name' => $contact->{'lastName'},
|
||||
'email' => $contact->{'email'},
|
||||
'phone' => $contact->{'phone'},
|
||||
'cdate' => $contact->{'cdate'},
|
||||
'ip' => $contact->{'ip'},
|
||||
'status' => 1, // @todo this should be checked based on the list membership.
|
||||
'tags' => array(),
|
||||
'lists' => array(),
|
||||
'fields' => array(),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// Fields.
|
||||
|
||||
if ( ! empty( $response->{'fieldValues'} ) ) {
|
||||
|
||||
foreach ( $response->{'fieldValues'} as $field ) {
|
||||
|
||||
if ( false !== strpos( $field->value, '||' ) ) {
|
||||
$type = 'checkbox';
|
||||
} else {
|
||||
$type = 'text';
|
||||
}
|
||||
|
||||
$subscribers[ $field->contact ]['fields'][] = array(
|
||||
'val' => $field->value,
|
||||
'perstag' => $field->field,
|
||||
'type' => $type,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Tags.
|
||||
|
||||
if ( ! empty( $response->{'contactTags'} ) ) {
|
||||
|
||||
foreach ( $response->{'contactTags'} as $tag ) {
|
||||
$subscribers[ $tag->contact ]['tags'][] = $tag->tag;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Lists.
|
||||
|
||||
if ( ! empty( $response->{'contactLists'} ) ) {
|
||||
|
||||
foreach ( $response->{'contactLists'} as $list ) {
|
||||
$subscribers[ $list->contact ]['lists'][] = array( 'listid' => $list->list );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $subscribers;
|
||||
|
||||
}
|
||||
|
||||
public function contactPaginator($args = ['limit' => 20, 'public' => 0])
|
||||
{
|
||||
return $this->make_request('contact_paginator', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all lists in the system.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function get_lists()
|
||||
{
|
||||
return $this->make_request('list_list', array('ids' => 'all'));
|
||||
}
|
||||
|
||||
public function getTags()
|
||||
{
|
||||
return $this->make_request('tags_list', array('ids' => 'all'));
|
||||
}
|
||||
|
||||
public function maybeError($response)
|
||||
{
|
||||
/* If invalid content type, API URL is invalid. */
|
||||
if (is_wp_error($response))
|
||||
return $response;
|
||||
$contentType = isset($response['headers']['content-type']) ? (string) $response['headers']['content-type'] : '';
|
||||
if (strpos($contentType, 'application/json') === false) {
|
||||
return new \WP_Error('error', 'Invalid API URL');
|
||||
}
|
||||
|
||||
if ($response['response']['code'] > 300) {
|
||||
return new \WP_Error('API_Error', $response['response']['message'], $response);
|
||||
}
|
||||
|
||||
$body = json_decode($response['body'], true);
|
||||
if (isset($body['result_code']) && $body['result_code'] == 0) {
|
||||
$message = 'Invalid API';
|
||||
return new \WP_Error('API_Error', $message, $response);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator\Api;
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class ConvertKit
|
||||
{
|
||||
protected $apiUrl = 'https://api.convertkit.com/v3/';
|
||||
|
||||
protected $apiKey = null;
|
||||
|
||||
protected $apiSecret = null;
|
||||
|
||||
public function __construct( $apiKey = null, $apiSecret = null )
|
||||
{
|
||||
$this->apiKey = $apiKey;
|
||||
$this->apiSecret = $apiSecret;
|
||||
}
|
||||
|
||||
public function default_options()
|
||||
{
|
||||
return array(
|
||||
'api_key' => $this->apiKey
|
||||
);
|
||||
}
|
||||
|
||||
public function make_request( $action, $options = array(), $method = 'GET' )
|
||||
{
|
||||
/* Build request options string. */
|
||||
$request_options = $this->default_options();
|
||||
|
||||
$request_options = wp_parse_args($options, $request_options);
|
||||
$options_string = http_build_query( $request_options );
|
||||
|
||||
/* Execute request based on method. */
|
||||
switch ( $method ) {
|
||||
case 'POST':
|
||||
$args = array(
|
||||
'body' => json_encode($options),
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json'
|
||||
]
|
||||
);
|
||||
$response = wp_remote_post( $this->apiUrl.$action.'?api_key='.$this->apiKey, $args );
|
||||
break;
|
||||
|
||||
case 'GET':
|
||||
/* Build request URL. */
|
||||
$request_url = $this->apiUrl . $action.'?' . $options_string;
|
||||
$response = wp_remote_get( $request_url );
|
||||
break;
|
||||
}
|
||||
|
||||
/* If WP_Error, die. Otherwise, return decoded JSON. */
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return [
|
||||
'error' => __('API_Error', 'fluent-crm'),
|
||||
'message' => $response->get_error_message()
|
||||
];
|
||||
} else {
|
||||
return json_decode( $response['body'], true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the provided API credentials.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function auth_test()
|
||||
{
|
||||
return $this->make_request('forms', [], 'GET');
|
||||
}
|
||||
|
||||
|
||||
public function subscribe($formId, $data)
|
||||
{
|
||||
$response = $this->make_request('forms/'.$formId.'/subscribe', $data, 'POST');
|
||||
if(!empty($response['error'])) {
|
||||
return new \WP_Error('api_error', $response['message']);
|
||||
}
|
||||
|
||||
return $response['subscription'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Forms in the system.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getLists()
|
||||
{
|
||||
$response = $this->make_request( 'forms', array(), 'GET' );
|
||||
if(empty($response['error'])) {
|
||||
return $response['forms'];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Tags in the system.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getTags()
|
||||
{
|
||||
$response = $this->make_request( 'tags', array(), 'GET' );
|
||||
if(empty($response['error'])) {
|
||||
return $response['tags'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getCustomFields()
|
||||
{
|
||||
$response = $this->make_request( 'custom_fields', array(), 'GET' );
|
||||
if(empty($response['error'])) {
|
||||
return $response['custom_fields'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSubscribers($args = [])
|
||||
{
|
||||
$args['api_secret'] = $this->apiSecret;
|
||||
$response = $this->make_request( 'subscribers', $args, 'GET' );
|
||||
|
||||
if(empty($response['error'])) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
new \WP_Error('api_error', $response['message']);
|
||||
}
|
||||
|
||||
public function getTagSubscribers($tagId, $args = [])
|
||||
{
|
||||
$args['api_secret'] = $this->apiSecret;
|
||||
$response = $this->make_request( 'tags/'.$tagId.'/subscriptions', $args, 'GET' );
|
||||
|
||||
if(empty($response['error'])) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
new \WP_Error('api_error', $response['message']);
|
||||
}
|
||||
|
||||
public function getSubscriberTags($contactId)
|
||||
{
|
||||
$args['api_secret'] = $this->apiSecret;
|
||||
$response = $this->make_request( 'subscribers/'.$contactId.'/tags', $args, 'GET' );
|
||||
|
||||
if(empty($response['error'])) {
|
||||
return $response['tags'];
|
||||
}
|
||||
|
||||
new \WP_Error('api_error', $response['message']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator\Api;
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Drip
|
||||
{
|
||||
protected $apiKey = null;
|
||||
protected $accountId = null;
|
||||
|
||||
private $apiUrl = "https://api.getdrip.com/v2/";
|
||||
|
||||
public function __construct($apiKey = null, $accountId = null)
|
||||
{
|
||||
$this->apiKey = $apiKey;
|
||||
$this->accountId = $accountId;
|
||||
}
|
||||
|
||||
public function make_request($endpoint = '', $data = array(), $method = 'POST')
|
||||
{
|
||||
$data['api_key'] = $this->apiKey;
|
||||
|
||||
$args = array(
|
||||
'method' => $method,
|
||||
'headers' => array(
|
||||
'content-type' => 'application/vnd.api+json',
|
||||
'Authorization' => 'Basic ' . base64_encode($this->apiKey)
|
||||
),
|
||||
'body' => ($method == 'POST') ? wp_json_encode($data) : $data
|
||||
);
|
||||
|
||||
if ($method == 'POST') {
|
||||
$response = wp_remote_post($this->apiUrl . $endpoint, $args);
|
||||
} else {
|
||||
$response = wp_remote_get($this->apiUrl . $endpoint, $args);
|
||||
}
|
||||
/* If WP_Error, die. Otherwise, return decoded JSON. */
|
||||
if (is_wp_error($response)) {
|
||||
return $response;
|
||||
}
|
||||
return json_decode($response['body'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the provided API credentials.
|
||||
*
|
||||
* @access public
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function auth_test()
|
||||
{
|
||||
return $this->make_request('accounts', [], 'GET');
|
||||
}
|
||||
|
||||
public function sendAccountItems($endpoint, $args = [])
|
||||
{
|
||||
return $this->make_request($this->accountId . '/'.$endpoint, $args, 'GET');
|
||||
}
|
||||
|
||||
public function addContact($contact)
|
||||
{
|
||||
$accountId = $this->accountId;
|
||||
$contactObj = [
|
||||
'subscribers' => [$contact]
|
||||
];
|
||||
$response = $this->make_request($accountId . '/subscribers', $contactObj, 'POST');
|
||||
|
||||
if (!empty($response['subscribers'])) {
|
||||
return $response;
|
||||
}
|
||||
$message = 'API Eroror';
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
$message = $response->get_error_message();
|
||||
}
|
||||
|
||||
return new \WP_Error('error', $message);
|
||||
}
|
||||
|
||||
public function add_note($contact_id, $email, $note)
|
||||
{
|
||||
return $this->make_request([
|
||||
'action' => 'contact_add_note',
|
||||
'value' => (object)[
|
||||
'contact_id' => $contact_id,
|
||||
'email' => $email,
|
||||
'note' => $note
|
||||
],
|
||||
], 'POST');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator\Api;
|
||||
|
||||
/**
|
||||
* Super-simple, minimum abstraction MailChimp API v3 wrapper
|
||||
* MailChimp API v3: http://developer.mailchimp.com
|
||||
* This wrapper: https://github.com/drewm/mailchimp-api
|
||||
*
|
||||
* @author Drew McLellan <drew.mclellan@gmail.com>
|
||||
* @version 2.4
|
||||
*/
|
||||
class MailChimp
|
||||
{
|
||||
private $api_key;
|
||||
private $api_endpoint = 'https://<dc>.api.mailchimp.com/3.0';
|
||||
|
||||
const TIMEOUT = 10;
|
||||
|
||||
/* SSL Verification
|
||||
Read before disabling:
|
||||
http://snippets.webaware.com.au/howto/stop-turning-off-curlopt_ssl_verifypeer-and-fix-your-php-config/
|
||||
*/
|
||||
public $verify_ssl = true;
|
||||
|
||||
private $request_successful = false;
|
||||
private $last_error = '';
|
||||
private $last_response = array();
|
||||
private $last_request = array();
|
||||
|
||||
/**
|
||||
* Create a new instance
|
||||
* @param string $api_key Your MailChimp API key
|
||||
* @param string $api_endpoint Optional custom API endpoint
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($api_key, $api_endpoint = null)
|
||||
{
|
||||
$this->api_key = $api_key;
|
||||
|
||||
if ($api_endpoint === null) {
|
||||
if (strpos($this->api_key, '-') === false) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||
throw new \Exception("Invalid MailChimp API key `{$api_key}` supplied.");
|
||||
}
|
||||
list(, $data_center) = explode('-', $this->api_key);
|
||||
$this->api_endpoint = str_replace('<dc>', $data_center, $this->api_endpoint);
|
||||
} else {
|
||||
$this->api_endpoint = $api_endpoint;
|
||||
}
|
||||
|
||||
$this->last_response = array('headers' => null, 'body' => null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The url to the API endpoint
|
||||
*/
|
||||
public function getApiEndpoint()
|
||||
{
|
||||
return $this->api_endpoint;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert an email address into a 'subscriber hash' for identifying the subscriber in a method URL
|
||||
* @param string $email The subscriber's email address
|
||||
* @return string Hashed version of the input
|
||||
*/
|
||||
public function subscriberHash($email)
|
||||
{
|
||||
return md5(strtolower($email));
|
||||
}
|
||||
|
||||
/**
|
||||
* Was the last request successful?
|
||||
* @return bool True for success, false for failure
|
||||
*/
|
||||
public function success()
|
||||
{
|
||||
return $this->request_successful;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last error returned by either the network transport, or by the API.
|
||||
* If something didn't work, this should contain the string describing the problem.
|
||||
* @return string|false describing the error
|
||||
*/
|
||||
public function getLastError()
|
||||
{
|
||||
return $this->last_error ?: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array containing the HTTP headers and the body of the API response.
|
||||
* @return array Assoc array with keys 'headers' and 'body'
|
||||
*/
|
||||
public function getLastResponse()
|
||||
{
|
||||
return $this->last_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array containing the HTTP headers and the body of the API request.
|
||||
* @return array Assoc array
|
||||
*/
|
||||
public function getLastRequest()
|
||||
{
|
||||
return $this->last_request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP DELETE request - for deleting data
|
||||
* @param string $method URL of the API request method
|
||||
* @param array $args Assoc array of arguments (if any)
|
||||
* @param int $timeout Timeout limit for request in seconds
|
||||
* @return array|false Assoc array of API response, decoded from JSON
|
||||
*/
|
||||
public function delete($method, $args = array(), $timeout = self::TIMEOUT)
|
||||
{
|
||||
return $this->makeRequest('delete', $method, $args, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP GET request - for retrieving data
|
||||
* @param string $method URL of the API request method
|
||||
* @param array $args Assoc array of arguments (usually your data)
|
||||
* @param int $timeout Timeout limit for request in seconds
|
||||
* @return array|false Assoc array of API response, decoded from JSON
|
||||
*/
|
||||
public function get($method, $args = array(), $timeout = self::TIMEOUT)
|
||||
{
|
||||
return $this->makeRequest('get', $method, $args, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP PATCH request - for performing partial updates
|
||||
* @param string $method URL of the API request method
|
||||
* @param array $args Assoc array of arguments (usually your data)
|
||||
* @param int $timeout Timeout limit for request in seconds
|
||||
* @return array|false Assoc array of API response, decoded from JSON
|
||||
*/
|
||||
public function patch($method, $args = array(), $timeout = self::TIMEOUT)
|
||||
{
|
||||
return $this->makeRequest('patch', $method, $args, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP POST request - for creating and updating items
|
||||
* @param string $method URL of the API request method
|
||||
* @param array $args Assoc array of arguments (usually your data)
|
||||
* @param int $timeout Timeout limit for request in seconds
|
||||
* @return array|false Assoc array of API response, decoded from JSON
|
||||
*/
|
||||
public function post($method, $args = array(), $timeout = self::TIMEOUT)
|
||||
{
|
||||
return $this->makeRequest('post', $method, $args, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP PUT request - for creating new items
|
||||
* @param string $method URL of the API request method
|
||||
* @param array $args Assoc array of arguments (usually your data)
|
||||
* @param int $timeout Timeout limit for request in seconds
|
||||
* @return array|false Assoc array of API response, decoded from JSON
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function put($method, $args = array(), $timeout = self::TIMEOUT)
|
||||
{
|
||||
return $this->makeRequest('put', $method, $args, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the underlying HTTP request. Not very exciting.
|
||||
* @param string $http_verb The HTTP verb to use: get, post, put, patch, delete
|
||||
* @param string $method The API method to be called
|
||||
* @param array $args Assoc array of parameters to be passed
|
||||
* @param int $timeout
|
||||
* @return array|false Assoc array of decoded result
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function makeRequest($http_verb, $method, $args = array(), $timeout = self::TIMEOUT)
|
||||
{
|
||||
if (!function_exists('curl_init') || !function_exists('curl_setopt')) {
|
||||
throw new \Exception("cURL support is required, but can't be found.");
|
||||
}
|
||||
|
||||
$url = $this->api_endpoint . '/' . $method;
|
||||
|
||||
$response = $this->prepareStateForRequest($http_verb, $method, $url, $timeout);
|
||||
|
||||
$httpHeader = array(
|
||||
'Accept: application/vnd.api+json',
|
||||
'Content-Type: application/vnd.api+json',
|
||||
'Authorization: apikey ' . $this->api_key
|
||||
);
|
||||
|
||||
if (isset($args["language"])) {
|
||||
$httpHeader[] = "Accept-Language: " . $args["language"];
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_init
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_setopt
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_exec
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_close
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_getinfo
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_error
|
||||
// PluginCheck:ignoreFile
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeader);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'DrewM/MailChimp-API/3.0 (github.com/drewm/mailchimp-api)');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
|
||||
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, '');
|
||||
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
|
||||
|
||||
switch ($http_verb) {
|
||||
case 'post':
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
$this->attachRequestPayload($ch, $args);
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
$query = http_build_query($args, '', '&');
|
||||
curl_setopt($ch, CURLOPT_URL, $url . '?' . $query);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
break;
|
||||
|
||||
case 'patch':
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
$this->attachRequestPayload($ch, $args);
|
||||
break;
|
||||
|
||||
case 'put':
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
$this->attachRequestPayload($ch, $args);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$responseContent = curl_exec($ch);
|
||||
$response['headers'] = curl_getinfo($ch);
|
||||
$response = $this->setResponseState($response, $responseContent, $ch);
|
||||
$formattedResponse = $this->formatResponse($response);
|
||||
|
||||
curl_close($ch);
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_init
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_setopt
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_exec
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_close
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_getinfo
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_error
|
||||
|
||||
$this->determineSuccess($response, $formattedResponse, $timeout);
|
||||
|
||||
return $formattedResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $http_verb
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param integer $timeout
|
||||
*/
|
||||
private function prepareStateForRequest($http_verb, $method, $url, $timeout)
|
||||
{
|
||||
$this->last_error = '';
|
||||
|
||||
$this->request_successful = false;
|
||||
|
||||
$this->last_response = array(
|
||||
'headers' => null, // array of details from curl_getinfo()
|
||||
'httpHeaders' => null, // array of HTTP headers
|
||||
'body' => null // content of the response
|
||||
);
|
||||
|
||||
$this->last_request = array(
|
||||
'method' => $http_verb,
|
||||
'path' => $method,
|
||||
'url' => $url,
|
||||
'body' => '',
|
||||
'timeout' => $timeout,
|
||||
);
|
||||
|
||||
return $this->last_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP headers as an array of header-name => header-value pairs.
|
||||
*
|
||||
* The "Link" header is parsed into an associative array based on the
|
||||
* rel names it contains. The original value is available under
|
||||
* the "_raw" key.
|
||||
*
|
||||
* @param string $headersAsString
|
||||
* @return array
|
||||
*/
|
||||
private function getHeadersAsArray($headersAsString)
|
||||
{
|
||||
$headers = array();
|
||||
|
||||
foreach (explode("\r\n", $headersAsString) as $i => $line) {
|
||||
if ($i === 0) { // HTTP code
|
||||
continue;
|
||||
}
|
||||
|
||||
$line = trim($line);
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($key, $value) = explode(': ', $line);
|
||||
|
||||
if ($key == 'Link') {
|
||||
$value = array_merge(
|
||||
array('_raw' => $value),
|
||||
$this->getLinkHeaderAsArray($value)
|
||||
);
|
||||
}
|
||||
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all rel => URL pairs from the provided Link header value
|
||||
*
|
||||
* Mailchimp only implements the URI reference and relation type from
|
||||
* RFC 5988, so the value of the header is something like this:
|
||||
*
|
||||
* 'https://us13.api.mailchimp.com/schema/3.0/Lists/Instance.json; rel="describedBy", <https://us13.admin.mailchimp.com/lists/members/?id=XXXX>; rel="dashboard"'
|
||||
*
|
||||
* @param string $linkHeaderAsString
|
||||
* @return array
|
||||
*/
|
||||
private function getLinkHeaderAsArray($linkHeaderAsString)
|
||||
{
|
||||
$urls = array();
|
||||
|
||||
if (preg_match_all('/<(.*?)>\s*;\s*rel="(.*?)"\s*/', $linkHeaderAsString, $matches)) {
|
||||
foreach ($matches[2] as $i => $relName) {
|
||||
$urls[$relName] = $matches[1][$i];
|
||||
}
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the data and attach it to the request
|
||||
* @param resource $ch cURL session handle, used by reference
|
||||
* @param array $data Assoc array of data to attach
|
||||
*/
|
||||
private function attachRequestPayload(&$ch, $data)
|
||||
{
|
||||
$encoded = json_encode($data);
|
||||
$this->last_request['body'] = $encoded;
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the response and format any error messages for debugging
|
||||
* @param array $response The response from the curl request
|
||||
* @return array|false The JSON decoded into an array
|
||||
*/
|
||||
private function formatResponse($response)
|
||||
{
|
||||
$this->last_response = $response;
|
||||
|
||||
if (!empty($response['body'])) {
|
||||
return json_decode($response['body'], true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do post-request formatting and setting state from the response
|
||||
* @param array $response The response from the curl request
|
||||
* @param string $responseContent The body of the response from the curl request
|
||||
* * @return array The modified response
|
||||
*/
|
||||
private function setResponseState($response, $responseContent, $ch)
|
||||
{
|
||||
if ($responseContent === false) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_error
|
||||
$this->last_error = curl_error($ch);
|
||||
} else {
|
||||
|
||||
$headerSize = $response['headers']['header_size'];
|
||||
|
||||
$response['httpHeaders'] = $this->getHeadersAsArray(substr($responseContent, 0, $headerSize));
|
||||
$response['body'] = substr($responseContent, $headerSize);
|
||||
|
||||
if (isset($response['headers']['request_header'])) {
|
||||
$this->last_request['headers'] = $response['headers']['request_header'];
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the response was successful or a failure. If it failed, store the error.
|
||||
* @param array $response The response from the curl request
|
||||
* @param array|false $formattedResponse The response body payload from the curl request
|
||||
* @param int $timeout The timeout supplied to the curl request.
|
||||
* @return bool If the request was successful
|
||||
*/
|
||||
private function determineSuccess($response, $formattedResponse, $timeout)
|
||||
{
|
||||
$status = $this->findHTTPStatus($response, $formattedResponse);
|
||||
|
||||
if ($status >= 200 && $status <= 299) {
|
||||
$this->request_successful = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($formattedResponse['detail'])) {
|
||||
$this->last_error = sprintf('%d: %s', $formattedResponse['status'], $formattedResponse['detail']);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( $timeout > 0 && $response['headers'] && $response['headers']['total_time'] >= $timeout ) {
|
||||
$this->last_error = sprintf('Request timed out after %f seconds.', $response['headers']['total_time'] );
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->last_error = 'Unknown error, call getLastResponse() to find out what happened.';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the HTTP status code from the headers or API response body
|
||||
* @param array $response The response from the curl request
|
||||
* @param array|false $formattedResponse The response body payload from the curl request
|
||||
* @return int HTTP status code
|
||||
*/
|
||||
private function findHTTPStatus($response, $formattedResponse)
|
||||
{
|
||||
if (!empty($response['headers']) && isset($response['headers']['http_code'])) {
|
||||
return (int) $response['headers']['http_code'];
|
||||
}
|
||||
|
||||
if (!empty($response['body']) && isset($formattedResponse['status'])) {
|
||||
return (int) $formattedResponse['status'];
|
||||
}
|
||||
|
||||
return 418;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator\Api;
|
||||
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class MailerLite
|
||||
{
|
||||
protected $apiUrl = 'https://api.mailerlite.com/api/v2/';
|
||||
|
||||
protected $apiKey = null;
|
||||
|
||||
protected $apiSecret = null;
|
||||
|
||||
public function __construct($apiKey = null)
|
||||
{
|
||||
$this->apiKey = $apiKey;
|
||||
}
|
||||
|
||||
public function default_options()
|
||||
{
|
||||
return [
|
||||
'User-Agent' => 'MailerLite PHP SDK/2.0',
|
||||
'X-MailerLite-ApiKey' => $this->apiKey,
|
||||
'Content-Type' => 'application/json'
|
||||
];
|
||||
}
|
||||
|
||||
public function make_request($action, $options = array(), $method = 'GET')
|
||||
{
|
||||
|
||||
$headers = $this->default_options();
|
||||
$endpointUrl = $this->apiUrl . $action;
|
||||
$args = [
|
||||
'headers' => $headers
|
||||
];
|
||||
|
||||
if ($options && $method == 'POST') {
|
||||
$args['body'] = \json_encode($options);
|
||||
} else if($method == 'GET' && $options) {
|
||||
$endpointUrl = add_query_arg($options, $endpointUrl);
|
||||
}
|
||||
|
||||
/* Execute request based on method. */
|
||||
switch ($method) {
|
||||
case 'POST':
|
||||
$response = wp_remote_post($endpointUrl, $args);
|
||||
break;
|
||||
|
||||
case 'GET':
|
||||
$response = wp_remote_get($endpointUrl, $args);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If WP_Error, die. Otherwise, return decoded JSON. */
|
||||
if (is_wp_error($response)) {
|
||||
return new \WP_Error('API_Error', $response->get_error_message());
|
||||
} else if ($response && $response['response']['code'] >= 300) {
|
||||
return new \WP_Error('API_Error', $response['response']['message']);
|
||||
}
|
||||
return json_decode($response['body'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the provided API credentials.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function auth_test()
|
||||
{
|
||||
return $this->make_request('groups', [], 'GET');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Forms in the system.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getGroups()
|
||||
{
|
||||
return $this->make_request('groups', array(), 'GET');
|
||||
}
|
||||
|
||||
public function getGroupSubscribers($groupId, $args = [])
|
||||
{
|
||||
return $this->make_request('groups/' . $groupId . '/subscribers', $args, 'GET');
|
||||
}
|
||||
|
||||
public function getContactCountByGroup($groupId)
|
||||
{
|
||||
$result = $this->make_request('groups/' . $groupId . '/subscribers/count', array(), 'GET');
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result['count'];
|
||||
}
|
||||
|
||||
public function getCustomFields()
|
||||
{
|
||||
return $this->make_request('fields', array(), 'GET');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator;
|
||||
|
||||
use FluentCrm\App\Models\Lists;
|
||||
use FluentCrm\App\Models\Tag;
|
||||
use FluentCrm\Framework\Support\Arr;
|
||||
|
||||
abstract class BaseMigrator
|
||||
{
|
||||
abstract public function getInfo();
|
||||
|
||||
abstract public function verifyCredentials($credential);
|
||||
|
||||
abstract public function getSummary($settings);
|
||||
|
||||
abstract public function runImport($settings);
|
||||
|
||||
abstract public function getListTagMappings($settings);
|
||||
|
||||
public function getFillables($with = ['main_fields', 'address', 'custom_fields'])
|
||||
{
|
||||
$mainFields = [
|
||||
'prefix' => __('Name Prefix', 'fluent-crm'),
|
||||
'first_name' => __('First Name', 'fluent-crm'),
|
||||
'last_name' => __('Last Name', 'fluent-crm'),
|
||||
'full_name' => __('Full Name', 'fluent-crm'),
|
||||
'phone' => __('Phone', 'fluent-crm')
|
||||
];
|
||||
|
||||
if (!$with) {
|
||||
return $mainFields;
|
||||
}
|
||||
|
||||
$addressFields = [
|
||||
'address_line_1' => __('Address Line 1', 'fluent-crm'),
|
||||
'address_line_2' => __('Address Line 2', 'fluent-crm'),
|
||||
'postal_code' => __('Postal Code', 'fluent-crm'),
|
||||
'city' => __('City', 'fluent-crm'),
|
||||
'state' => __('State', 'fluent-crm'),
|
||||
'country' => __('Country', 'fluent-crm'),
|
||||
];
|
||||
|
||||
$customFields = fluentcrm_get_custom_contact_fields();
|
||||
|
||||
$customs = [];
|
||||
foreach ($customFields as $customField) {
|
||||
$customs[$customField['slug']] = $customField['label'] . ' (Custom Field)';
|
||||
}
|
||||
|
||||
if (in_array('address', $with)) {
|
||||
$mainFields = array_merge($mainFields, $addressFields);
|
||||
}
|
||||
|
||||
if ($customs && in_array('custom_fields', $with)) {
|
||||
$mainFields = array_merge($mainFields, $customs);
|
||||
}
|
||||
|
||||
if (in_array('custom_fields_only', $with)) {
|
||||
return $customs;
|
||||
}
|
||||
|
||||
return $mainFields;
|
||||
|
||||
}
|
||||
|
||||
public function getMergedData($remoteData, $fieldMaps)
|
||||
{
|
||||
$formattedData = [];
|
||||
foreach ($fieldMaps as $fieldMap) {
|
||||
if ($fieldMap['will_skip'] == 'yes' || empty($fieldMap['fluentcrm_field']) || empty($fieldMap['remote_tag'])) {
|
||||
continue;
|
||||
}
|
||||
$tagName = $fieldMap['remote_tag'];
|
||||
if (empty($remoteData[$tagName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fluentCrmTag = $fieldMap['fluentcrm_field'];
|
||||
|
||||
if (!empty($fieldMap['date_format'])) {
|
||||
$givenValue = $remoteData[$tagName];
|
||||
$myDateTime = \DateTime::createFromFormat($fieldMap['date_format'], $givenValue);
|
||||
$formattedData[$fluentCrmTag] = $myDateTime->format('Y-m-d');
|
||||
} else {
|
||||
$formattedData[$fluentCrmTag] = $remoteData[$tagName];
|
||||
}
|
||||
}
|
||||
|
||||
$formattedData = array_filter($formattedData);
|
||||
|
||||
$customFields = fluentcrm_get_custom_contact_fields();
|
||||
|
||||
if (!$customFields) {
|
||||
return $formattedData;
|
||||
}
|
||||
|
||||
$keys = array_map(function ($item) {
|
||||
return $item['slug'];
|
||||
}, $customFields);
|
||||
|
||||
$customs = array_filter(Arr::only($formattedData, $keys));
|
||||
|
||||
foreach ($keys as $key) {
|
||||
unset($formattedData[$key]);
|
||||
}
|
||||
|
||||
$formattedData['custom_values'] = $customs;
|
||||
|
||||
return $formattedData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe create field options.
|
||||
*
|
||||
* When importing multi-checkbox or multi-select data, we can save time by
|
||||
* auto-populating the options into the FluentCRM custom field config.
|
||||
*
|
||||
* @param array $mergeData The loaded custom field data.
|
||||
* @since 2.8.34
|
||||
*
|
||||
*/
|
||||
public function maybeCreateFieldOptions($mergeData)
|
||||
{
|
||||
|
||||
$needsUpdate = false;
|
||||
$customFields = fluentcrm_get_custom_contact_fields();
|
||||
|
||||
foreach ($mergeData['custom_values'] as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($customFields as $i => $field) {
|
||||
if ($key === $field['slug'] && in_array($field['type'], ['checkbox', 'select-multi']) && !empty(array_diff($value, $field['options']))) {
|
||||
$value = array_map('sanitize_text_field', $value);
|
||||
$customFields[$i]['options'] = array_merge($field['options'], array_diff($value, $field['options']));
|
||||
$needsUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($needsUpdate) {
|
||||
fluentcrm_update_option('contact_custom_fields', $customFields);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function mapTags($tagMappings)
|
||||
{
|
||||
$formattedMaps = [];
|
||||
|
||||
foreach ($tagMappings as $tagMapping) {
|
||||
$remoteId = $tagMapping['remote_id'];
|
||||
if ($tagMapping['will_create'] == 'yes') {
|
||||
$remoteName = sanitize_text_field($tagMapping['remote_name']);
|
||||
if (!$remoteName) {
|
||||
continue;
|
||||
}
|
||||
$tagMapping['fluentcrm_id'] = $this->getTagId($remoteName);
|
||||
}
|
||||
|
||||
if (empty($tagMapping['fluentcrm_id'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$formattedMaps[$remoteId] = (int)$tagMapping['fluentcrm_id'];
|
||||
}
|
||||
|
||||
return $formattedMaps;
|
||||
|
||||
}
|
||||
|
||||
public function getTagId($tagName)
|
||||
{
|
||||
$slug = sanitize_title($tagName);
|
||||
|
||||
$tag = Tag::updateOrCreate(
|
||||
['slug' => sanitize_title($slug, 'display')],
|
||||
['title' => $tagName]
|
||||
);
|
||||
|
||||
do_action('fluentcrm_tag_created', $tag->id);
|
||||
|
||||
do_action('fluent_crm/tag_created', $tag);
|
||||
|
||||
return $tag->id;
|
||||
}
|
||||
|
||||
public function getListId($listName)
|
||||
{
|
||||
$slug = sanitize_title($listName);
|
||||
|
||||
$list = Lists::updateOrCreate(
|
||||
['slug' => sanitize_title($slug, 'display')],
|
||||
['title' => $listName]
|
||||
);
|
||||
|
||||
do_action('fluentcrm_list_created', $list->id);
|
||||
do_action('fluent_crm/list_created', $list);
|
||||
|
||||
return $list->id;
|
||||
}
|
||||
|
||||
public function getInfoIcon()
|
||||
{
|
||||
$infoSvg = '<span class="fc-inline-help-icon" aria-hidden="true" style="display:inline-flex;vertical-align:middle">'
|
||||
. '<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">'
|
||||
. '<path d="M8 1.333a6.667 6.667 0 1 0 0 13.334A6.667 6.667 0 0 0 8 1.333zm0 2a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm1.333 9.334H6.667v-1.333h1.333V7.333H6.667V6h2.666v5.333h1.333v1.334z"/>'
|
||||
. '</svg></span>';
|
||||
|
||||
return $infoSvg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator;
|
||||
|
||||
use FluentCrm\App\Services\CrmMigrator\Api\ConvertKit;
|
||||
use FluentCrm\Framework\Support\Arr;
|
||||
|
||||
class ConvertKitMigrator extends BaseMigrator
|
||||
{
|
||||
public function getInfo()
|
||||
{
|
||||
$infoSvg = $this->getInfoIcon();
|
||||
|
||||
$logo = $this->getSvgLogo();
|
||||
|
||||
return [
|
||||
'title' => 'Kit',
|
||||
'formerly' => 'ConvertKit',
|
||||
'description' => __('Migrate your ConvertKit contacts and associate to FluentCRM', 'fluent-crm'),
|
||||
'logo' => fluentCrmMix('images/migrators/convertkit.png'),
|
||||
'logo_svg' => $logo,
|
||||
'supports' => [
|
||||
'tags' => true,
|
||||
'lists' => false,
|
||||
'empty_tags' => false,
|
||||
'active_imports_only' => false
|
||||
],
|
||||
'field_map_info' => __('Email Address and First name will be mapped automatically', 'fluent-crm'),
|
||||
'tags_map_info' => __('Only Selected tags will be imported from ConvertKit', 'fluent-crm'),
|
||||
'credentials' => [
|
||||
'api_key' => '',
|
||||
'api_secret' => ''
|
||||
],
|
||||
'credential_fields' => [
|
||||
'api_key' => [
|
||||
'label' => __('API Key', 'fluent-crm'),
|
||||
'placeholder' => __('ConvertKit API Key', 'fluent-crm'),
|
||||
'data_type' => 'text',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find your API key at ConvertKit ', 'fluent-crm') . '<a href="https://app.convertkit.com/account_settings/advanced_settings" rel="noopener" target="_blank">'. __("Account -> Settings -> Advanced", "fluent-crm") .'</a>'
|
||||
],
|
||||
'api_secret' => [
|
||||
'label' => __('API Secret', 'fluent-crm'),
|
||||
'placeholder' => __('ConvertKit API Secret', 'fluent-crm'),
|
||||
'data_type' => 'password',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find your API Secret key at ConvertKit Account -> Settings -> Advanced', 'fluent-crm')
|
||||
]
|
||||
],
|
||||
'refresh_on_list_change' => false,
|
||||
'doc_url' => 'https://fluentcrm.com/docs/migrating-into-fluentcrm-from-convertkit/'
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyCredentials($credential)
|
||||
{
|
||||
$api = $this->getApi($credential);
|
||||
|
||||
try {
|
||||
$result = $api->auth_test();
|
||||
if (!empty($result['error'])) {
|
||||
throw new \Exception($result['message']);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
return new \WP_Error('api_error', $exception->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getListTagMappings($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
$tags = $api->getTags();
|
||||
|
||||
$formattedTags = [];
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$formattedTags[] = [
|
||||
'remote_name' => $tag['name'],
|
||||
'remote_id' => (string)$tag['id'],
|
||||
'will_create' => 'no',
|
||||
'fluentcrm_id' => ''
|
||||
];
|
||||
}
|
||||
|
||||
$data['tags'] = $formattedTags;
|
||||
|
||||
$contactFields = $api->getCustomFields();
|
||||
$formattedContactFields = [];
|
||||
|
||||
foreach ($contactFields as $field) {
|
||||
$item = [
|
||||
'type' => 'any',
|
||||
'remote_label' => $field['label'],
|
||||
'remote_tag' => $field['key'],
|
||||
'fluentcrm_field' => '',
|
||||
'will_skip' => 'no'
|
||||
];
|
||||
|
||||
$fieldKey = $field['key'];
|
||||
|
||||
if ($fieldKey == 'last_name') {
|
||||
$item['fluentcrm_field'] = 'last_name';
|
||||
}
|
||||
|
||||
$formattedContactFields[] = $item;
|
||||
}
|
||||
|
||||
$data['contact_fields'] = $formattedContactFields;
|
||||
$data['contact_fillables'] = $this->getFillables();
|
||||
|
||||
unset($data['contact_fillables']['first_name']);
|
||||
unset($data['contact_fillables']['full_name']);
|
||||
|
||||
$data['all_ready'] = true;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSummary($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$subscribers = $api->getSubscribers([
|
||||
'page' => 1
|
||||
]);
|
||||
|
||||
if (is_wp_error($subscribers)) {
|
||||
return $subscribers;
|
||||
}
|
||||
|
||||
$totalSubscribers = Arr::get($subscribers, 'total_subscribers', 0);
|
||||
|
||||
|
||||
$tagMappings = Arr::get($postedData, 'tags', []);
|
||||
|
||||
$tagCounts = 0;
|
||||
|
||||
foreach ($tagMappings as $tagMapping) {
|
||||
if ($tagMapping['will_create'] == 'yes') {
|
||||
$tagCounts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tagMapping['will_create'] == 'no' || empty($tagMapping['fluentcrm_id'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tagCounts++;
|
||||
|
||||
}
|
||||
|
||||
$message = __('Based on your selections, ', 'fluent-crm') . $tagCounts . __(' tags and associate contacts will be imported from ConvertKit', 'fluent-crm');
|
||||
|
||||
|
||||
return [
|
||||
'subscribed_count' => $totalSubscribers,
|
||||
'unsubscribed_count' => 0,
|
||||
'all_count' => $totalSubscribers,
|
||||
'message' => $message
|
||||
];
|
||||
}
|
||||
|
||||
public function runImport($postedData)
|
||||
{
|
||||
if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) {
|
||||
define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true);
|
||||
}
|
||||
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$tagMappings = Arr::get($postedData, 'tags', []);
|
||||
|
||||
$taggingArray = $this->mapTags($tagMappings);
|
||||
|
||||
$taggingKeys = array_keys($taggingArray);
|
||||
|
||||
if (!$taggingKeys) {
|
||||
return new \WP_Error('not_found', 'No Tag found based on your selection');
|
||||
}
|
||||
|
||||
$import_tracker = Arr::get($postedData, 'import_tracker', []);
|
||||
|
||||
if (empty($import_tracker)) {
|
||||
$import_tracker = [
|
||||
'current_index' => 0,
|
||||
'completed_page' => 0
|
||||
];
|
||||
}
|
||||
|
||||
$currentTagId = $taggingKeys[$import_tracker['current_index']];
|
||||
$completedPage = $import_tracker['completed_page'];
|
||||
|
||||
$members = $api->getTagSubscribers($currentTagId, [
|
||||
'page' => $completedPage + 1
|
||||
]);
|
||||
|
||||
$subscribers = $members['subscriptions'];
|
||||
|
||||
$fieldMaps = Arr::get($postedData, 'contact_fields');
|
||||
|
||||
$mapSettings = Arr::get($postedData, 'map_settings', []);
|
||||
|
||||
foreach ($subscribers as $subscriberItem) {
|
||||
|
||||
$subscriber = $subscriberItem['subscriber'];
|
||||
if ($subscriber['state'] != 'active') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'email' => $subscriber['email_address'],
|
||||
'first_name' => $subscriber['first_name'],
|
||||
'created_at' => gmdate('Y-m-d H:i:s', strtotime($subscriber['created_at'])),
|
||||
'source' => 'ConvertKit',
|
||||
'status' => 'subscribed'
|
||||
];
|
||||
|
||||
$mergeData = $this->getMergedData($subscriber['fields'], $fieldMaps);
|
||||
|
||||
if ($mergeData) {
|
||||
$data = array_merge($data, $mergeData);
|
||||
}
|
||||
|
||||
if (!empty($mapSettings['local_list_id'])) {
|
||||
$data['lists'] = [$mapSettings['local_list_id']];
|
||||
}
|
||||
|
||||
$data['tags'] = [$taggingArray[$currentTagId]];
|
||||
|
||||
$contact = FluentCrmApi('contacts')->createOrUpdate($data);
|
||||
|
||||
if ($contact && $contact->status != 'subscribed') {
|
||||
$contact->updateStatus('subscribed');
|
||||
}
|
||||
}
|
||||
|
||||
$stepCompleted = ($completedPage + 1) >= $members['total_pages'];
|
||||
|
||||
if (!$stepCompleted) {
|
||||
$import_tracker = [
|
||||
'current_index' => $import_tracker['current_index'],
|
||||
'completed_page' => $completedPage + 1
|
||||
];
|
||||
} else {
|
||||
$nextIndex = $import_tracker['current_index'] + 1;
|
||||
$import_tracker = [
|
||||
'current_index' => $nextIndex,
|
||||
'completed_page' => 0
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'completed' => 0,
|
||||
'total' => 0,
|
||||
'import_tracker' => $import_tracker,
|
||||
'has_more' => isset($taggingKeys[$import_tracker['current_index']]),
|
||||
'message' => __('Importer is running now. ', 'fluent-crm').( $import_tracker['current_index']+1 ) .__(' tags have been imported so far', 'fluent-crm')
|
||||
];
|
||||
}
|
||||
|
||||
private function getApi($credential)
|
||||
{
|
||||
return new ConvertKit($credential['api_key'], $credential['api_secret']);
|
||||
}
|
||||
|
||||
public function getSvgLogo()
|
||||
{
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="71" height="32" viewBox="0 0 71 32" fill="none">
|
||||
<g clip-path="url(#clip0_6972_60083)">
|
||||
<mask id="mask0_6972_60083" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="71" height="32">
|
||||
<path d="M70.9328 0H0V32H70.9328V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_6972_60083)">
|
||||
<mask id="mask1_6972_60083" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="71" height="32">
|
||||
<path d="M70.961 0H0V32H70.961V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_6972_60083)">
|
||||
<path d="M19.436 13.3155C28.1679 15.0036 30.8781 23.0778 30.9496 31.1988C30.9512 31.3815 30.8035 31.5305 30.6206 31.5305H19.6288C19.4475 31.5305 19.2998 31.3843 19.2988 31.2028C19.2658 24.9024 18.2438 19.3425 11.9939 19.1024C11.8074 19.0954 11.652 19.2445 11.652 19.431V31.202C11.652 31.3834 11.5047 31.5305 11.323 31.5305H0.329015C0.147368 31.5305 0 31.3837 0 31.202V1.20928C0 1.02784 0.147368 0.88064 0.329015 0.88064H11.323C11.5047 0.88064 11.652 1.02784 11.652 1.20928V12.4445C11.652 12.6112 11.7872 12.7463 11.9541 12.7463C12.0864 12.7463 12.204 12.6601 12.2425 12.5335C15.0744 3.26849 20.3634 0.93888 28.9517 0.8816C29.1341 0.88032 29.2833 1.02816 29.2833 1.21024V12.4176C29.2833 12.599 29.136 12.7463 28.9543 12.7463H19.491C19.3322 12.7463 19.2033 12.8749 19.2033 13.0336C19.2033 13.1712 19.301 13.2896 19.436 13.3155ZM49.5337 20.3389V13.0749C49.5337 12.8934 49.681 12.7463 49.8627 12.7463H57.9529C58.1121 12.7463 58.2412 12.6173 58.2412 12.4583C58.2412 12.3203 58.1429 12.2022 58.0074 12.1757C51.6775 10.9216 48.7555 7.28192 48.6546 1.2096C48.6518 1.02912 48.7968 0.88064 48.9772 0.88064H60.8567C61.0384 0.88064 61.1857 1.02784 61.1857 1.20928V6.32481C61.1857 6.50624 61.333 6.65344 61.5148 6.65344H68.3263C68.508 6.65344 68.6553 6.80064 68.6553 6.98208V12.4176C68.6553 12.599 68.508 12.7463 68.3263 12.7463H61.5148C61.333 12.7463 61.1857 12.8934 61.1857 13.0749V18.9341C61.1857 21.0041 62.4563 21.6867 64.146 21.6867C66.7937 21.6867 69.4056 20.4951 70.4532 19.9549C70.6724 19.842 70.9329 20.001 70.9329 20.247V29.3683C70.9329 29.6119 70.7983 29.8359 70.5823 29.9494C69.5483 30.4928 66.3509 31.9994 62.6933 31.9994C55.1711 32 49.5337 28.937 49.5337 20.3389ZM34.0697 31.202V13.0743C34.0697 12.8928 34.217 12.7456 34.3987 12.7456H45.3927C45.5743 12.7456 45.7217 12.8928 45.7217 13.0743V31.202C45.7217 31.3834 45.5743 31.5305 45.3927 31.5305H34.3987C34.217 31.5305 34.0697 31.3837 34.0697 31.202ZM33.4471 5.58688C33.4471 8.67232 35.6286 11.1738 39.8196 11.1738C44.0105 11.1738 46.192 8.67232 46.192 5.58688C46.192 2.50144 44.0103 0 39.8196 0C35.6286 0 33.4471 2.50144 33.4471 5.58688Z" fill="#1E1E1E"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_6972_60083">
|
||||
<rect width="71" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator;
|
||||
|
||||
use FluentCrm\App\Services\CrmMigrator\Api\Drip;
|
||||
use FluentCrm\Framework\Support\Arr;
|
||||
|
||||
class DripMigrator extends BaseMigrator
|
||||
{
|
||||
public function getInfo()
|
||||
{
|
||||
$infoSvg = $this->getInfoIcon();
|
||||
|
||||
$logo = $this->getSvgLogo();
|
||||
|
||||
return [
|
||||
'title' => 'Drip',
|
||||
'description' => __('Transfer your Drip tags and contacts to FluentCRM', 'fluent-crm'),
|
||||
'logo' => fluentCrmMix('images/migrators/drip.png'),
|
||||
'logo_svg' => $logo,
|
||||
'supports' => [
|
||||
'tags' => true,
|
||||
'lists' => false,
|
||||
'empty_tags' => true,
|
||||
'active_imports_only' => true
|
||||
],
|
||||
'credentials' => [
|
||||
'api_key' => '',
|
||||
'account_id' => ''
|
||||
],
|
||||
'field_map_info' => __('Email and main contact fields will be mapped automatically', 'fluent-crm'),
|
||||
'credential_fields' => [
|
||||
'api_key' => [
|
||||
'label' => __('API Token', 'fluent-crm'),
|
||||
'placeholder' => __('Drip API Token', 'fluent-crm'),
|
||||
'data_type' => 'password',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find your API key at Drip Profile -> User Info -> API Token', 'fluent-crm')
|
||||
],
|
||||
'account_id' => [
|
||||
'label' => __('Account ID', 'fluent-crm'),
|
||||
'placeholder' => __('Drip Account ID', 'fluent-crm'),
|
||||
'data_type' => 'text',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find Account ID Settings -> General Info -> Account ID', 'fluent-crm')
|
||||
]
|
||||
],
|
||||
'refresh_on_list_change' => false,
|
||||
'doc_url' => 'https://fluentcrm.com/docs/migrating-into-fluentcrm-from-drip/'
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyCredentials($credential)
|
||||
{
|
||||
$api = $this->getApi($credential);
|
||||
|
||||
try {
|
||||
$result = $api->make_request('accounts', [], 'GET');
|
||||
if (is_wp_error($result)) {
|
||||
return $result;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
return new \WP_Error('api_error', $exception->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getListTagMappings($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$tags = $api->sendAccountItems('tags');
|
||||
|
||||
$data = [];
|
||||
|
||||
if (is_wp_error($tags)) {
|
||||
return $tags;
|
||||
}
|
||||
|
||||
$formattedTags = [];
|
||||
|
||||
foreach ($tags['tags'] as $tag) {
|
||||
$formattedTags[] = [
|
||||
'remote_name' => (string)$tag,
|
||||
'remote_id' => (string)$tag,
|
||||
'will_create' => 'no',
|
||||
'fluentcrm_id' => ''
|
||||
];
|
||||
}
|
||||
$data['tags'] = $formattedTags;
|
||||
|
||||
$mergeFields = $api->sendAccountItems('custom_field_identifiers');
|
||||
|
||||
$contactFields = $mergeFields['custom_field_identifiers'];
|
||||
$formattedContactFields = [];
|
||||
|
||||
$defaultFields = [
|
||||
'First_Name',
|
||||
'Last_Name',
|
||||
'address1',
|
||||
'address2',
|
||||
'city',
|
||||
'phone',
|
||||
'state',
|
||||
'zip',
|
||||
'country'
|
||||
];
|
||||
|
||||
$contactFields = array_values(array_diff($contactFields, $defaultFields));
|
||||
|
||||
foreach ($contactFields as $field) {
|
||||
$item = [
|
||||
'type' => 'any',
|
||||
'remote_label' => $field,
|
||||
'remote_tag' => $field,
|
||||
'fluentcrm_field' => '',
|
||||
'remote_type' => '',
|
||||
'will_skip' => 'no'
|
||||
];
|
||||
|
||||
$formattedContactFields[] = $item;
|
||||
}
|
||||
|
||||
$data['contact_fields'] = $formattedContactFields;
|
||||
$data['contact_fillables'] = $this->getFillables();
|
||||
|
||||
$data['all_ready'] = true;
|
||||
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSummary($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$status = 'all';
|
||||
|
||||
$settings = Arr::get($postedData, 'map_settings', []);
|
||||
|
||||
if (Arr::get($settings, 'import_active_only') == 'yes') {
|
||||
$status = 'active';
|
||||
}
|
||||
|
||||
$members = $api->sendAccountItems('subscribers', [
|
||||
'status' => $status,
|
||||
'page' => 1,
|
||||
'per_page' => 10
|
||||
]);
|
||||
|
||||
if (is_wp_error($members)) {
|
||||
return $members;
|
||||
}
|
||||
|
||||
$meta = $members['meta'];
|
||||
|
||||
$message = __('Based on your selections ', 'fluent-crm') . $meta['total_count'] . __(' contacts will be imported', 'fluent-crm');
|
||||
|
||||
return [
|
||||
'subscribed_count' => $meta['total_count'],
|
||||
'unsubscribed_count' => 0,
|
||||
'all_count' => $meta['total_count'],
|
||||
'message' => $message
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
public function runImport($postedData)
|
||||
{
|
||||
if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) {
|
||||
define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true);
|
||||
}
|
||||
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$processPerPage = 10;
|
||||
|
||||
$page = Arr::get($postedData, 'completed', 0);
|
||||
|
||||
if (!$page) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'page' => $page,
|
||||
'per_page' => $processPerPage,
|
||||
'status' => 'all'
|
||||
];
|
||||
|
||||
$tagMappings = Arr::get($postedData, 'tags', []);
|
||||
|
||||
$taggingArray = $this->mapTags($tagMappings);
|
||||
|
||||
$mapSettings = Arr::get($postedData, 'map_settings', []);
|
||||
|
||||
if ($mapSettings['import_active_only'] == 'yes') {
|
||||
$params['status'] = 'subscribed';
|
||||
}
|
||||
|
||||
$members = $api->sendAccountItems('subscribers', $params);
|
||||
|
||||
if (is_wp_error($members)) {
|
||||
return $members;
|
||||
}
|
||||
|
||||
$memberMeta = $members['meta'];
|
||||
|
||||
$subscribers = $members['subscribers'];
|
||||
|
||||
$fieldMaps = Arr::get($postedData, 'contact_fields', []);
|
||||
|
||||
foreach ($subscribers as $subscriber) {
|
||||
|
||||
$statusMaps = [
|
||||
'active' => 'subscribed',
|
||||
'unsubscribed' => 'unsubscribed'
|
||||
];
|
||||
$status = (isset($statusMaps[$subscriber['status']])) ? $statusMaps[$subscriber['status']] : 'pending';
|
||||
if ($mapSettings['import_active_only'] == 'yes') {
|
||||
$status = 'subscribed';
|
||||
}
|
||||
|
||||
$data = [
|
||||
'email' => $subscriber['email'],
|
||||
'first_name' => $subscriber['first_name'],
|
||||
'last_name' => $subscriber['last_name'],
|
||||
'address_line_1' => $subscriber['address1'],
|
||||
'address_line_2' => $subscriber['address2'],
|
||||
'city' => $subscriber['city'],
|
||||
'state' => $subscriber['state'],
|
||||
'postal_code' => $subscriber['zip'],
|
||||
'phone' => $subscriber['phone'],
|
||||
'created_at' => gmdate('Y-m-d H:i:s', strtotime($subscriber['created_at'])),
|
||||
'source' => 'Drip',
|
||||
'ip' => $subscriber['ip_address'],
|
||||
'country' => Arr::get($subscriber, 'country'),
|
||||
'status' => $status
|
||||
];
|
||||
|
||||
$mergeData = $this->getMergedData($subscriber['custom_fields'], $fieldMaps);
|
||||
|
||||
if ($mergeData) {
|
||||
$data = array_merge($data, $mergeData);
|
||||
}
|
||||
|
||||
if (!empty($mapSettings['local_list_id'])) {
|
||||
$data['lists'] = [$mapSettings['local_list_id']];
|
||||
}
|
||||
|
||||
if (!empty($subscriber['tags'])) {
|
||||
$tagIds = [];
|
||||
foreach ($subscriber['tags'] as $contactTag) {
|
||||
if (!empty($taggingArray[$contactTag])) {
|
||||
$tagIds[] = $taggingArray[$contactTag];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($tagIds) && !empty($mapSettings['local_tag_id'])) {
|
||||
$tagIds = [$mapSettings['local_tag_id']];
|
||||
}
|
||||
|
||||
$data['tags'] = $tagIds;
|
||||
|
||||
} else if (!empty($mapSettings['local_tag_id'])) {
|
||||
$data['tags'] = [$mapSettings['local_tag_id']];
|
||||
}
|
||||
|
||||
$contact = FluentCrmApi('contacts')->createOrUpdate($data);
|
||||
|
||||
if ($status == 'subscribed' && $contact && $contact->status != 'subscribed') {
|
||||
$contact->updateStatus('subscribed');
|
||||
}
|
||||
}
|
||||
|
||||
$completed = $memberMeta['page'] + 1;
|
||||
|
||||
return [
|
||||
'completed' => $completed,
|
||||
'total' => $memberMeta['total_pages'],
|
||||
'has_more' => $memberMeta['page'] < $memberMeta['total_pages']
|
||||
];
|
||||
}
|
||||
|
||||
private function getApi($credentials)
|
||||
{
|
||||
return new Drip($credentials['api_key'], $credentials['account_id']);
|
||||
}
|
||||
|
||||
public function getSvgLogo()
|
||||
{
|
||||
return '<svg class="logo nav__logo" height="30" viewBox="0 0 84 30" width="84" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Drip</title>
|
||||
<g class="logo__logomark nav__logo-logomark" id="logomark">
|
||||
<path d="M18 14.5L15.3 14.5C15.3 14.6 15.3 14.7 15.3 14.8 15.3 18.8 12 21.2 9 21.2 6 21.2 2.7 18.8 2.7 14.8 2.7 14.7 2.7 14.6 2.7 14.5L0 14.5C0 14.6 0 14.7 0 14.8 0 20.3 4.5 23.9 9 23.9 13.5 23.9 18 20.3 18 14.8 18 14.7 18 14.6 18 14.5Z" id="logo-mouth"></path>
|
||||
<path d="M9 3.4L10.9 6 14.3 6C12.7 3.9 11 1.8 9 0 7 1.8 5.3 3.9 3.7 6L7.1 6 9 3.4Z" id="logo-hat"></path>
|
||||
<g id="logo-eyes">
|
||||
<ellipse cx="3.6" cy="10.3" id="logo-eye-left" rx="1.8" ry="1.8"></ellipse>
|
||||
<ellipse cx="9" cy="10.3" id="logo-eye-center" rx="1.8" ry="1.8"></ellipse>
|
||||
<ellipse cx="14.4" cy="10.3" id="logo-eye-right" rx="1.8" ry="1.8"></ellipse>
|
||||
</g>
|
||||
</g>
|
||||
<g class="logo__wordmark nav__logo-wordmark" id="wordmark">
|
||||
<path d="M40.8 23.5L37.6 23.5 37.6 20.9C35.9 23 33.6 24 30.9 24 28.2 24 25.8 23.1 24.2 21.5 22.6 19.9 21.6 17.6 21.6 14.9 21.6 12.1 22.5 9.9 24.2 8.3 25.9 6.7 28.2 5.8 30.9 5.8 33.8 5.8 35.8 6.7 37.6 8.7L37.6 0 40.8 0 40.8 23.5ZM31.2 8.6C27.3 8.6 24.9 10.9 24.9 14.9 24.9 18.8 27.3 21.1 31.2 21.1 35.2 21.1 37.6 18.8 37.6 14.9 37.6 11 35.2 8.6 31.2 8.6Z" id="wordmark-d"></path>
|
||||
<polygon id="wordmark-r" points="55.2 8.9 47.5 8.9 47.5 24 44.4 24 44.4 6 55.2 6"></polygon>
|
||||
<path d="M57.6 1.9C57.6 1.2 57.9 0.6 58.5 0.3 59.1-0.1 59.7-0.1 60.3 0.3 60.9 0.6 61.2 1.2 61.2 1.9 61.2 2.6 60.9 3.3 60.3 3.6 59.7 4 59.1 4 58.5 3.6 57.9 3.3 57.6 2.6 57.6 1.9ZM57.9 6.2L60.9 6.2 60.9 24 57.9 24 57.9 6.2Z" id="wordmark-i"></path>
|
||||
<path d="M64.8 6.4L68 6.4 68 9C69.6 7.1 71.7 6 74.7 6 77.4 6 79.7 6.9 81.4 8.5 83.1 10.1 84 12.4 84 15.1 84 17.9 83.1 20.1 81.4 21.7 79.7 23.3 77.4 24.2 74.6 24.2 71.8 24.2 69.8 23.3 68 21.3L68 30 64.8 30 64.8 6.4ZM74.3 21.4C78.3 21.4 80.7 19.1 80.7 15.1 80.7 11.2 78.3 8.9 74.3 8.9 70.4 8.9 68 11.2 68 15.1 68 19 70.4 21.4 74.3 21.4Z" id="wordmark-p"></path>
|
||||
</g>
|
||||
</svg>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator;
|
||||
|
||||
use FluentCrm\App\Models\Tag;
|
||||
use FluentCrm\App\Services\CrmMigrator\Api\MailChimp;
|
||||
use FluentCrm\Framework\Support\Arr;
|
||||
|
||||
class MailChimpMigrator extends BaseMigrator
|
||||
{
|
||||
private $tagNameCache = [];
|
||||
private $taggingArray = [];
|
||||
|
||||
public function getInfo()
|
||||
{
|
||||
$infoSvg = $this->getInfoIcon();
|
||||
|
||||
$logo = $this->getSvgLogo();
|
||||
|
||||
return [
|
||||
'title' => 'MailChimp',
|
||||
'description' => __('Transfer your mailchimp lists, tags and contacts from MailChimp to FluentCRM', 'fluent-crm'),
|
||||
'logo' => fluentCrmMix('images/migrators/mailchimp.png'),
|
||||
'logo_svg' => $logo,
|
||||
'supports' => [
|
||||
'tags' => true,
|
||||
'lists' => true,
|
||||
'empty_tags' => true,
|
||||
'active_imports_only' => true,
|
||||
'auto_tag_mapper' => true
|
||||
],
|
||||
'credentials' => [
|
||||
'api_key' => ''
|
||||
],
|
||||
'credential_fields' => [
|
||||
'api_key' => [
|
||||
'label' => __('API Key', 'fluent-crm'),
|
||||
'placeholder' => __('MailChimp API Key', 'fluent-crm'),
|
||||
'data_type' => 'password',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find your API key at MailChimp Account -> Extras -> API keys', 'fluent-crm')
|
||||
]
|
||||
],
|
||||
'refresh_on_list_change' => true,
|
||||
'doc_url' => 'https://fluentcrm.com/docs/migrating-into-fluentcrm-from-mailchimp/'
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyCredentials($credential)
|
||||
{
|
||||
$api = $this->getApi($credential);
|
||||
try {
|
||||
$result = $api->get('lists');
|
||||
if (!$api->success()) {
|
||||
throw new \Exception($api->getLastError());
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
return new \WP_Error('api_error', $exception->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getListTagMappings($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
$lists = $api->get('lists', ['count' => 9999]);
|
||||
$formattedLists = [];
|
||||
|
||||
foreach ($lists['lists'] as $list) {
|
||||
$formattedLists[] = [
|
||||
'id' => $list['id'],
|
||||
'name' => $list['name'],
|
||||
'description' => __('(Contacts count ', 'fluent-crm') . Arr::get($list, 'stats.member_count') . ')'
|
||||
];
|
||||
}
|
||||
|
||||
$data = [
|
||||
'lists' => $formattedLists,
|
||||
'tags' => [],
|
||||
'contact_fields' => [],
|
||||
'contact_fillables' => []
|
||||
];
|
||||
|
||||
$settings = Arr::get($postedData, 'map_settings', []);
|
||||
|
||||
if (!empty($settings['list_id'])) {
|
||||
$tags = $api->get('lists/' . $settings['list_id'] . '/tag-search?count=9999');
|
||||
$formattedTags = [];
|
||||
|
||||
foreach ($tags['tags'] as $tag) {
|
||||
$formattedTags[] = [
|
||||
'remote_name' => (string)$tag['name'],
|
||||
'remote_id' => (string)$tag['id'],
|
||||
'will_create' => 'no',
|
||||
'fluentcrm_id' => ''
|
||||
];
|
||||
}
|
||||
$data['tags'] = $formattedTags;
|
||||
$mergeFields = $api->get('lists/' . $settings['list_id'] . '/merge-fields', array('count' => 9999));
|
||||
|
||||
$contactFields = $mergeFields['merge_fields'];
|
||||
|
||||
$mcFieldTypes = ['text', 'radio', 'phone', 'imageurl', 'url', 'dropdown', 'date', 'number', 'address'];
|
||||
$formattedContactFields = [];
|
||||
|
||||
$addressMapped = false;
|
||||
foreach ($contactFields as $field) {
|
||||
$fieldType = $field['type'];
|
||||
|
||||
if (!in_array($fieldType, $mcFieldTypes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = [
|
||||
'type' => 'any',
|
||||
'remote_label' => $field['name'],
|
||||
'remote_tag' => $field['tag'],
|
||||
'fluentcrm_field' => '',
|
||||
'remote_type' => $fieldType,
|
||||
'will_skip' => 'no'
|
||||
];
|
||||
|
||||
if ($fieldType == 'address') {
|
||||
$item['type'] = 'selections';
|
||||
$item['options'] = ['contact_address' => 'Contact Address'];
|
||||
|
||||
if (!$addressMapped) {
|
||||
$addressMapped = true;
|
||||
$item['fluentcrm_field'] = 'contact_address';
|
||||
}
|
||||
} else if ($fieldType == 'date') {
|
||||
$item['date_format'] = $field['options']['date_format'];
|
||||
}
|
||||
|
||||
if ($item['remote_tag'] == 'FNAME') {
|
||||
$item['fluentcrm_field'] = 'full_name';
|
||||
} else if ($item['remote_label'] == 'First Name') {
|
||||
$item['fluentcrm_field'] = 'first_name';
|
||||
} else if ($item['remote_label'] == 'Last Name') {
|
||||
$item['fluentcrm_field'] = 'last_name';
|
||||
}
|
||||
|
||||
$formattedContactFields[] = $item;
|
||||
}
|
||||
|
||||
$data['contact_fields'] = $formattedContactFields;
|
||||
$data['contact_fillables'] = $this->getFillables();
|
||||
|
||||
$data['all_ready'] = true;
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSummary($postedData)
|
||||
{
|
||||
$settings = Arr::get($postedData, 'map_settings', []);
|
||||
$listId = Arr::get($settings, 'list_id');
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$list = $api->get('lists/' . $listId);
|
||||
|
||||
$membersCount = Arr::get($list, 'stats.member_count', 0);
|
||||
$allCount = Arr::get($list, 'stats.unsubscribe_count') + $membersCount;
|
||||
|
||||
$count = $allCount;
|
||||
|
||||
if (Arr::get($settings, 'import_active_only') == 'yes') {
|
||||
$count = $membersCount;
|
||||
}
|
||||
|
||||
$message = __('Based on your selections ', 'fluent-crm') . $count . __(' contacts will be imported', 'fluent-crm');
|
||||
|
||||
return [
|
||||
'subscribed_count' => $membersCount,
|
||||
'unsubscribed_count' => Arr::get($list, 'stats.unsubscribe_count', 0),
|
||||
'all_count' => $allCount,
|
||||
'message' => $message
|
||||
];
|
||||
}
|
||||
|
||||
public function runImport($postedData)
|
||||
{
|
||||
|
||||
$isAutoTagMapping = Arr::get($postedData, 'auto_mapping') == 'yes';
|
||||
|
||||
if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) {
|
||||
define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true);
|
||||
}
|
||||
|
||||
$mapSettings = Arr::get($postedData, 'map_settings', []);
|
||||
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$processPerPage = 100;
|
||||
|
||||
$params = [
|
||||
'offset' => Arr::get($postedData, 'completed', 0),
|
||||
'count' => $processPerPage
|
||||
];
|
||||
|
||||
if(!$isAutoTagMapping) {
|
||||
$tagMappings = Arr::get($postedData, 'tags', []);
|
||||
$this->taggingArray = $this->mapTags($tagMappings);
|
||||
}
|
||||
|
||||
if ($mapSettings['import_active_only'] == 'yes') {
|
||||
$params['status'] = 'subscribed';
|
||||
}
|
||||
|
||||
$members = $api->get('lists/' . $mapSettings['list_id'] . '/members', $params);
|
||||
|
||||
$subscribers = $members['members'];
|
||||
|
||||
$fieldMaps = Arr::get($postedData, 'contact_fields', []);
|
||||
|
||||
foreach ($subscribers as $subscriber) {
|
||||
$created_at = gmdate('Y-m-d H:i:s');
|
||||
if (!empty($subscriber['timestamp_signup'])) {
|
||||
$created_at = gmdate('Y-m-d H:i:s', strtotime($subscriber['timestamp_signup']));
|
||||
}
|
||||
|
||||
$data = [
|
||||
'email' => $subscriber['email_address'],
|
||||
'full_name' => $subscriber['full_name'],
|
||||
'created_at' => $created_at,
|
||||
'source' => $subscriber['source'],
|
||||
'ip' => $subscriber['ip_signup'],
|
||||
'country' => Arr::get($subscriber, 'location.country_code'),
|
||||
'status' => $subscriber['status']
|
||||
];
|
||||
|
||||
$mergeData = $this->getMergedData($subscriber['merge_fields'], $fieldMaps);
|
||||
|
||||
if (!empty($mergeData['contact_address']) && is_array($mergeData['contact_address'])) {
|
||||
$address = $mergeData['contact_address'];
|
||||
unset($mergeData['contact_address']);
|
||||
|
||||
$address = array_filter($address);
|
||||
|
||||
$addressMappings = [
|
||||
'addr1' => 'address_line_1',
|
||||
'addr2' => 'address_line_2',
|
||||
'city' => 'city',
|
||||
'country' => 'country',
|
||||
'state' => 'state',
|
||||
'zip' => 'postal_code'
|
||||
];
|
||||
|
||||
foreach ($address as $key => $value) {
|
||||
if (isset($addressMappings[$key])) {
|
||||
$mergeData[$addressMappings[$key]] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($mergeData) {
|
||||
$data = array_merge($data, $mergeData);
|
||||
}
|
||||
|
||||
if (!empty($mapSettings['local_list_id'])) {
|
||||
$data['lists'] = [$mapSettings['local_list_id']];
|
||||
}
|
||||
|
||||
$data['tags'] = $this->formatRemoteTags($subscriber['tags'], $isAutoTagMapping, $mapSettings);
|
||||
|
||||
$contact = FluentCrmApi('contacts')->createOrUpdate($data);
|
||||
|
||||
if (isset($params['status']) && $params['status'] == 'subscribed' && $contact && $contact->status != 'subscribed') {
|
||||
$contact->updateStatus('subscribed');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$completed = Arr::get($postedData, 'completed', 0) + $processPerPage;
|
||||
|
||||
return [
|
||||
'completed' => $completed,
|
||||
'total' => $members['total_items'],
|
||||
'has_more' => $completed < $members['total_items']
|
||||
];
|
||||
}
|
||||
|
||||
private function formatRemoteTags($remoteTags, $isAuto, $mapSettings)
|
||||
{
|
||||
$tagIds = [];
|
||||
if($isAuto) {
|
||||
foreach ($remoteTags as $tag) {
|
||||
$tagName = sanitize_text_field($tag['name']);
|
||||
if(!empty($this->tagNameCache[$tagName])) {
|
||||
$tagIds[] = $this->tagNameCache[$tagName];
|
||||
} else {
|
||||
$createdTag = Tag::updateOrCreate(
|
||||
['slug' => sanitize_title($tagName, 'display')],
|
||||
['title' => sanitize_text_field($tagName)]
|
||||
);
|
||||
if($createdTag) {
|
||||
$this->tagNameCache[$createdTag->title] = $createdTag->id;
|
||||
$tagIds[] = $createdTag->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($tagIds) && !empty($mapSettings['local_tag_id'])) {
|
||||
$tagIds = [$mapSettings['local_tag_id']];
|
||||
}
|
||||
|
||||
return $tagIds;
|
||||
}
|
||||
|
||||
foreach ($remoteTags as $contactTag) {
|
||||
if (!empty($this->taggingArray[$contactTag['id']])) {
|
||||
$tagIds[] = $this->taggingArray[$contactTag['id']];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($tagIds) && !empty($mapSettings['local_tag_id'])) {
|
||||
$tagIds = [$mapSettings['local_tag_id']];
|
||||
}
|
||||
return $tagIds;
|
||||
}
|
||||
|
||||
private function getApi($credential)
|
||||
{
|
||||
return new MailChimp($credential['api_key']);
|
||||
}
|
||||
|
||||
public function getSvgLogo()
|
||||
{
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||
<path d="M16 2C8.27813 2 2 8.27812 2 16C2 23.7219 8.27813 30 16 30C23.7219 30 30 23.7219 30 16C30 8.27812 23.7219 2 16 2Z" fill="#FDDD4C"/>
|
||||
<path d="M9.86884 19.6502C9.56272 19.1867 10.3327 18.4853 10.0195 17.8963C9.83897 17.5567 9.54389 17.345 9.18952 17.3006C8.84941 17.2579 8.49905 17.3824 8.27573 17.6257C7.92315 18.0094 7.86832 18.5317 7.93652 18.7165C7.96148 18.7842 8.00071 18.8027 8.02879 18.8067C8.22083 18.832 8.32889 18.3391 8.39519 18.2364C8.57851 17.9535 8.99476 17.8679 9.28223 18.0538C9.82731 18.4064 9.35467 18.9762 9.39545 19.4574C9.43513 19.926 9.72709 20.1144 9.98919 20.1342C10.2442 20.1438 10.4225 20.0022 10.4675 19.8988C10.5754 19.6519 10.1207 20.0315 9.86884 19.6502Z" fill="black"/>
|
||||
<path d="M21.2577 15.3958C21.1147 15.3761 20.9585 15.3765 20.7959 15.3958C20.6725 15.2401 20.5619 14.9879 20.4995 14.6936C20.3885 14.17 20.4001 13.7907 20.7099 13.7414C21.0197 13.6922 21.1694 14.009 21.2804 14.5326C21.3549 14.8846 21.3406 15.208 21.2577 15.3958Z" fill="black"/>
|
||||
<path d="M17.8646 15.7074C17.8769 15.8259 17.8812 15.9459 17.8777 16.0564C18.1775 16.074 18.3898 16.2163 18.4463 16.3067C18.4753 16.3533 18.4637 16.3837 18.4544 16.3977C18.4233 16.446 18.3568 16.4386 18.2175 16.4229C18.0958 16.4093 17.9648 16.3973 17.8286 16.4035C17.7545 16.6308 17.5348 16.6521 17.3804 16.484C17.2726 16.5168 17.0608 16.6521 16.9976 16.5051C16.9971 16.4322 17.0734 16.3261 17.2115 16.2325C17.1172 16.0526 17.054 15.8601 17.0167 15.6607C16.8209 15.696 16.6447 15.7508 16.5066 15.7938C16.4418 15.814 16.1853 15.9299 16.1552 15.7993C16.1351 15.7091 16.2755 15.5604 16.424 15.453C16.5898 15.3353 16.7738 15.2516 16.9646 15.2033C16.9605 14.9193 17.0329 14.7211 17.2393 14.6883C17.4951 14.6476 17.6537 14.8446 17.7634 15.2093C18.0726 15.2952 18.3814 15.5082 18.5177 15.7284C18.5707 15.8138 18.581 15.8798 18.5466 15.9146C18.4609 16.0034 17.9862 15.7459 17.8646 15.7074Z" fill="black"/>
|
||||
<path d="M19.8977 16.9838C20.0938 17.0788 20.3096 17.0414 20.38 16.9002C20.4504 16.7589 20.3484 16.5675 20.1522 16.4725C19.9561 16.3775 19.7404 16.4149 19.6699 16.5561C19.5995 16.6973 19.7016 16.8887 19.8977 16.9838Z" fill="black"/>
|
||||
<path d="M20.8632 16.2824C20.8667 16.0664 20.9991 15.8939 21.1583 15.8965C21.3174 15.8996 21.4435 16.0765 21.44 16.2921C21.4364 16.5077 21.304 16.6802 21.1449 16.6775C20.9857 16.6749 20.8596 16.498 20.8632 16.2824Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.1785 18.9281C25.1772 18.9236 25.1816 18.9392 25.1785 18.9281C25.4981 18.9281 26 19.2907 26 20.1667C26 21.0384 25.6336 22.0257 25.5471 22.2449C24.2273 25.3671 21.0772 27.1051 17.3248 26.9951C13.8266 26.8926 10.8432 25.0705 9.53762 22.1001C8.7482 22.101 7.93426 21.7582 7.31556 21.217C6.66343 20.6468 6.26137 19.9089 6.18292 19.1393C6.12185 18.5401 6.19629 17.9826 6.38662 17.4964L5.65337 16.883C2.29777 14.0864 12.7929 2.57165 16.1494 5.46156C16.1663 5.47608 17.2914 6.56551 17.2941 6.56815C18.9125 5.88985 23.2489 4.5984 23.2537 7.60479C23.2555 8.60315 22.611 9.76738 21.5782 10.8238C22.7718 11.9164 22.457 13.4685 22.6471 14.8925L23.0616 15.006C23.8519 15.2247 24.414 15.5164 24.6891 15.8015C24.9641 16.0862 25.1005 16.3621 25.15 16.6855C25.1963 16.9464 25.1901 17.4071 24.8415 17.9223C24.967 18.2547 25.0816 18.5861 25.1785 18.9281ZM9.47834 21.2901C9.59557 21.2927 9.71191 21.2852 9.82602 21.2659C11.0523 21.0595 11.3732 19.7448 11.1713 18.4538C10.9431 16.9961 9.94414 16.4822 9.26616 16.4457C9.07761 16.436 8.90243 16.4527 8.75801 16.4813C7.5478 16.722 6.86447 17.7384 6.99908 19.0584C7.12077 20.253 8.3448 21.2601 9.47834 21.2901ZM6.73743 16.8395C7.14351 16.2574 7.80722 15.8363 8.56054 15.6915C9.51934 13.151 11.1205 10.8102 13.2396 9.19934C14.8122 7.90443 16.5082 6.97559 16.5082 6.97559C16.5082 6.97559 15.5949 5.93016 15.319 5.85316C13.622 5.40084 9.95707 7.89563 7.61689 11.1921C6.67012 12.5257 5.3146 14.8876 5.96272 16.1025C6.04251 16.253 6.49494 16.6397 6.73743 16.8395ZM20.4955 20.7066C20.4981 20.7339 20.4812 20.7621 20.4567 20.7722C20.4567 20.7722 19.0931 21.3983 16.9272 20.737C17.0051 21.3862 17.7924 21.6322 18.3425 21.7213C21.0263 22.1771 23.5355 20.6622 24.1002 20.2807C24.1972 20.2152 24.099 20.3836 24.0815 20.4083C23.3901 21.2887 21.5314 22.3082 19.1132 22.3078C18.0585 22.3073 17.0044 21.9408 16.6174 21.3772C16.017 20.5029 16.5876 19.2265 17.5883 19.3593C19.2798 19.5479 21.0139 19.4062 22.5798 18.6888C23.9451 18.0631 24.4608 17.375 24.3833 16.8175C24.263 15.9535 22.9821 15.8184 22.3346 15.6084C22.0529 15.5164 21.9138 15.4429 21.8822 14.9198C21.8684 14.6914 21.8282 13.8946 21.8135 13.565C21.7877 12.9882 21.7172 12.1993 21.2216 11.8737C21.0923 11.7887 20.9488 11.7478 20.7977 11.7399C20.6772 11.7341 20.6057 11.7507 20.5673 11.7596C20.5587 11.7616 20.5517 11.7632 20.5463 11.7641C20.2786 11.809 20.1146 11.9444 19.9212 12.1041C19.9101 12.1133 19.8988 12.1226 19.8875 12.1319C19.2692 12.6406 18.7472 12.7237 18.1664 12.6991C17.9855 12.6915 17.7991 12.6735 17.6024 12.6544C17.4215 12.6369 17.232 12.6185 17.0302 12.6067L16.7837 12.5926C15.8111 12.5433 14.768 13.3723 14.5946 14.5497C14.4009 15.864 15.1355 16.669 15.6132 17.1925C15.7313 17.3219 15.8337 17.434 15.9025 17.5325C15.9466 17.5919 15.9979 17.6755 15.9979 17.7551C15.9979 17.8502 15.9354 17.9254 15.8744 17.9896C14.8821 18.9968 14.5648 20.5971 14.9388 21.9307C14.9856 22.097 15.0448 22.2563 15.1148 22.4085C15.9921 24.4321 18.7138 25.3746 21.3722 24.5174C23.2354 23.9167 24.8816 22.465 25.2168 20.4624C25.2966 19.9406 25.1794 19.7391 25.0198 19.6418C24.8509 19.5393 24.6485 19.5749 24.6485 19.5749C24.6485 19.5749 24.5562 18.9515 24.295 18.3848C23.5199 18.9884 22.5223 19.4126 21.7627 19.6277C20.5458 19.9724 19.2312 20.1065 17.9737 19.944C17.4636 19.8781 17.1209 19.8338 16.974 20.3049C18.6514 20.9112 20.4268 20.6516 20.4268 20.6516C20.4611 20.6481 20.4919 20.6727 20.4955 20.7066ZM15.656 8.94062C14.7386 9.40394 13.7143 10.2289 12.8825 11.178C12.8531 11.2119 12.8968 11.2585 12.9329 11.2326C13.6514 10.7165 14.6365 10.2369 15.927 9.92622C17.3725 9.57818 18.7642 9.72426 19.6142 9.91654C19.657 9.92622 19.6837 9.85362 19.6459 9.8325C19.0842 9.52142 18.2221 9.31022 17.6106 9.30582C17.5807 9.30538 17.5638 9.27106 17.5816 9.2473C17.6872 9.10694 17.8321 8.96834 17.9645 8.86802C17.9939 8.84514 17.9761 8.79807 17.9386 8.80026C17.1779 8.84638 15.4402 9.63869 15.4465 9.60986C15.491 9.39866 15.6314 9.1197 15.7041 8.98946C15.7215 8.95866 15.6876 8.92478 15.656 8.94062Z" fill="black"/>
|
||||
</svg>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace FluentCrm\App\Services\CrmMigrator;
|
||||
|
||||
use FluentCrm\App\Services\CrmMigrator\Api\MailerLite;
|
||||
use FluentCrm\Framework\Support\Arr;
|
||||
|
||||
class MailerLiteMigrator extends BaseMigrator
|
||||
{
|
||||
public function getInfo()
|
||||
{
|
||||
$infoSvg = $this->getInfoIcon();
|
||||
|
||||
$logo = $this->getSvgLogo();
|
||||
|
||||
return [
|
||||
'title' => 'MailerLite',
|
||||
'description' => __('Migrate your MailerLite contacts and associate to FluentCRM', 'fluent-crm'),
|
||||
'logo' => fluentCrmMix('images/migrators/mailerlite.png'),
|
||||
'logo_svg' => $logo,
|
||||
'supports' => [
|
||||
'tags' => true,
|
||||
'lists' => false,
|
||||
'empty_tags' => false,
|
||||
'active_imports_only' => true
|
||||
],
|
||||
'field_map_info' => __('Email Address and First name will be mapped automatically', 'fluent-crm'),
|
||||
'tags_map_info' => __('Only Selected Groups will be imported from MailerLite', 'fluent-crm'),
|
||||
'credentials' => [
|
||||
'api_key' => ''
|
||||
],
|
||||
'credential_fields' => [
|
||||
'api_key' => [
|
||||
'label' => __('API Key', 'fluent-crm'),
|
||||
'placeholder' => __('MailerLite API Key', 'fluent-crm'),
|
||||
'data_type' => 'password',
|
||||
'type' => 'input-text',
|
||||
'inline_help' => $infoSvg . ' ' . __('You can find your API key at MailerLite', 'fluent-crm') . ' <a href="https://app.mailerlite.com/integrations/api/" target="_blank" rel="noopener">'. __("Account -> Integrations -> Developer API", "fluent-crm") .'</a>'
|
||||
]
|
||||
],
|
||||
'refresh_on_list_change' => false,
|
||||
'doc_url' => 'https://fluentcrm.com/docs/migrating-into-fluentcrm-from-mailerlite/'
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyCredentials($credential)
|
||||
{
|
||||
$api = $this->getApi($credential);
|
||||
|
||||
try {
|
||||
$result = $api->auth_test();
|
||||
if (is_wp_error($result)) {
|
||||
return $result;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
return new \WP_Error('api_error', $exception->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getListTagMappings($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$groups = $api->getGroups();
|
||||
|
||||
$formattedTags = [];
|
||||
|
||||
foreach ($groups as $tag) {
|
||||
$formattedTags[] = [
|
||||
'remote_name' => (string)$tag['name'],
|
||||
'remote_id' => (string)$tag['id'],
|
||||
'will_create' => 'no',
|
||||
'fluentcrm_id' => ''
|
||||
];
|
||||
}
|
||||
|
||||
$data['tags'] = $formattedTags;
|
||||
|
||||
$contactFields = $api->getCustomFields();
|
||||
$formattedContactFields = [];
|
||||
|
||||
$autoFieldMaps = [
|
||||
'name' => 'first_name',
|
||||
'last_name' => 'last_name',
|
||||
'country' => 'country',
|
||||
'city' => 'city',
|
||||
'phone' => 'phone',
|
||||
'state' => 'state',
|
||||
'zip' => 'postal_code'
|
||||
];
|
||||
|
||||
foreach ($contactFields as $field) {
|
||||
$item = [
|
||||
'type' => 'any',
|
||||
'remote_label' => $field['title'],
|
||||
'remote_tag' => $field['key'],
|
||||
'fluentcrm_field' => '',
|
||||
'will_skip' => 'no'
|
||||
];
|
||||
|
||||
|
||||
$fieldKey = $field['key'];
|
||||
|
||||
if ($fieldKey == 'email') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($autoFieldMaps[$fieldKey])) {
|
||||
$item['fluentcrm_field'] = $autoFieldMaps[$fieldKey];
|
||||
}
|
||||
|
||||
$formattedContactFields[] = $item;
|
||||
}
|
||||
|
||||
$data['contact_fields'] = $formattedContactFields;
|
||||
$data['contact_fillables'] = $this->getFillables();
|
||||
|
||||
$data['all_ready'] = true;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSummary($postedData)
|
||||
{
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$groups = $api->getGroups();
|
||||
|
||||
if (is_wp_error($groups)) {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
$tagMappings = Arr::get($postedData, 'tags', []);
|
||||
|
||||
$tagCounts = 0;
|
||||
|
||||
foreach ($tagMappings as $tagMapping) {
|
||||
if ($tagMapping['will_create'] == 'yes') {
|
||||
$tagCounts++;
|
||||
continue;
|
||||
}
|
||||
if ($tagMapping['will_create'] == 'no' || empty($tagMapping['fluentcrm_id'])) {
|
||||
continue;
|
||||
}
|
||||
$tagCounts++;
|
||||
}
|
||||
|
||||
$message = __('Based on your selections, ', 'fluent-crm') . $tagCounts . __(' groups and associate contacts will be imported from MailerLite', 'fluent-crm');
|
||||
|
||||
return [
|
||||
'subscribed_count' => 1,
|
||||
'unsubscribed_count' => 0,
|
||||
'all_count' => 1,
|
||||
'message' => $message
|
||||
];
|
||||
}
|
||||
|
||||
public function runImport($postedData)
|
||||
{
|
||||
if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) {
|
||||
define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true);
|
||||
}
|
||||
|
||||
$api = $this->getApi($postedData['credential']);
|
||||
|
||||
$tagMappings = Arr::get($postedData, 'tags', []);
|
||||
|
||||
$taggingArray = $this->mapTags($tagMappings);
|
||||
|
||||
$taggingKeys = array_keys($taggingArray);
|
||||
|
||||
if (!$taggingKeys) {
|
||||
return new \WP_Error('not_found', 'No Tag found based on your selection');
|
||||
}
|
||||
|
||||
$limitPerChunk = 50;
|
||||
|
||||
$import_tracker = Arr::get($postedData, 'import_tracker', []);
|
||||
|
||||
if (empty($import_tracker)) {
|
||||
$import_tracker = [
|
||||
'current_index' => 0,
|
||||
'offset' => 0
|
||||
];
|
||||
}
|
||||
|
||||
$currentTagId = $taggingKeys[$import_tracker['current_index']];
|
||||
|
||||
$subscribers = $api->getGroupSubscribers($currentTagId, [
|
||||
'offset' => $import_tracker['offset'],
|
||||
'limit' => $limitPerChunk
|
||||
]);
|
||||
|
||||
if (is_wp_error($subscribers)) {
|
||||
return $subscribers;
|
||||
}
|
||||
|
||||
$fieldMaps = Arr::get($postedData, 'contact_fields', []);
|
||||
|
||||
$mapSettings = Arr::get($postedData, 'map_settings', []);
|
||||
|
||||
foreach ($subscribers as $subscriber) {
|
||||
|
||||
if ($subscriber['type'] != 'active') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'email' => $subscriber['email'],
|
||||
'first_name' => $subscriber['name'],
|
||||
'created_at' => gmdate('Y-m-d H:i:s', strtotime($subscriber['date_created'])),
|
||||
'source' => 'MailerLite',
|
||||
'status' => 'subscribed',
|
||||
'ip' => $subscriber['signup_ip']
|
||||
];
|
||||
|
||||
$remoteData = $subscriber['fields'];
|
||||
|
||||
$formattedRemoteData = [];
|
||||
|
||||
foreach ($remoteData as $remoteDatum) {
|
||||
$formattedRemoteData[$remoteDatum['key']] = $remoteDatum['value'];
|
||||
}
|
||||
|
||||
$mergeData = $this->getMergedData($formattedRemoteData, $fieldMaps);
|
||||
|
||||
if ($mergeData) {
|
||||
$data = array_merge($data, $mergeData);
|
||||
}
|
||||
|
||||
if (!empty($mapSettings['local_list_id'])) {
|
||||
$data['lists'] = [$mapSettings['local_list_id']];
|
||||
}
|
||||
|
||||
$data['tags'] = [$taggingArray[$currentTagId]];
|
||||
|
||||
$contact = FluentCrmApi('contacts')->createOrUpdate($data);
|
||||
|
||||
if ($contact && $contact->status != 'subscribed') {
|
||||
$contact->updateStatus('subscribed');
|
||||
}
|
||||
}
|
||||
|
||||
$stepCompletedCount = $limitPerChunk + $import_tracker['offset'];
|
||||
|
||||
$groupContactsCount = $api->getContactCountByGroup($currentTagId);
|
||||
|
||||
if (is_wp_error($groupContactsCount)) {
|
||||
return $groupContactsCount;
|
||||
}
|
||||
$stepCompleted = $stepCompletedCount >= $groupContactsCount;
|
||||
|
||||
if (!$stepCompleted) {
|
||||
$import_tracker = [
|
||||
'current_index' => $import_tracker['current_index'],
|
||||
'offset' => $stepCompletedCount
|
||||
];
|
||||
} else {
|
||||
$nextIndex = $import_tracker['current_index'] + 1;
|
||||
$import_tracker = [
|
||||
'current_index' => $nextIndex,
|
||||
'offset' => 0
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'completed' => 0,
|
||||
'total' => 0,
|
||||
'import_tracker' => $import_tracker,
|
||||
'has_more' => isset($taggingKeys[$import_tracker['current_index']]),
|
||||
'message' => __('Importer is running now. ', 'fluent-crm').( $import_tracker['current_index']+1 ) .__(' tags have been imported so far', 'fluent-crm')
|
||||
];
|
||||
}
|
||||
|
||||
private function getApi($credential)
|
||||
{
|
||||
return new MailerLite($credential['api_key']);
|
||||
}
|
||||
|
||||
public function getSvgLogo()
|
||||
{
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="26" viewBox="0 0 32 26" fill="none">
|
||||
<g clip-path="url(#clip0_6972_60099)">
|
||||
<path d="M28.3562 0H3.50365C1.56496 0 0 1.56937 0 3.51351V25.836L4.83504 21.0342H28.3796C30.3182 21.0342 31.8832 19.4649 31.8832 17.5207V3.51351C31.8599 1.56937 30.2949 0 28.3562 0ZM7.45109 15.7405C7.45109 16.2559 7.03066 16.6775 6.51679 16.6775C6.00292 16.6775 5.58248 16.2559 5.58248 15.7405V5.78559C5.58248 5.27027 6.00292 4.84865 6.51679 4.84865C7.03066 4.84865 7.45109 5.27027 7.45109 5.78559V15.7405ZM11.7956 15.7405C11.7956 16.2559 11.3752 16.6775 10.8613 16.6775C10.3474 16.6775 9.92701 16.2559 9.92701 15.7405V9.15856C9.92701 8.64324 10.3474 8.22162 10.8613 8.22162C11.3752 8.22162 11.7956 8.64324 11.7956 9.15856V15.7405ZM11.9124 6.16036C11.9124 6.72252 11.4686 7.16757 10.908 7.16757H10.8146C10.254 7.16757 9.81022 6.72252 9.81022 6.16036V6.09009C9.81022 5.52793 10.254 5.08288 10.8146 5.08288H10.908C11.4686 5.08288 11.9124 5.52793 11.9124 6.09009V6.16036ZM18.4993 16.4432C18.0788 16.6541 17.6117 16.7477 17.1212 16.7477C15.5095 16.7477 14.6453 15.9748 14.6453 14.4991V9.97838H13.8044C13.5241 9.97838 13.2905 9.76757 13.2905 9.48649V9.46306C13.2905 9.2991 13.3839 9.13513 13.5241 9.01802L15.6029 6.98018C15.7197 6.86306 15.8599 6.79279 16.0234 6.76937C16.3037 6.76937 16.5606 7.0036 16.5606 7.28468V7.30811V8.29189H18.0555C18.5226 8.29189 18.8963 8.66667 18.8963 9.13513C18.8963 9.6036 18.5226 9.97838 18.0555 9.97838H16.5606V14.382C16.5606 15.0144 16.8876 15.0613 17.3314 15.0613C17.5183 15.0613 17.7051 15.0378 17.8686 14.991C17.9854 14.9441 18.1255 14.9441 18.2423 14.9207C18.6628 14.9207 19.0131 15.2721 19.0365 15.7171C18.9898 16.0216 18.7796 16.3261 18.4993 16.4432ZM24.2219 14.991C24.8993 15.0144 25.5766 14.8504 26.1839 14.5459C26.3007 14.4757 26.4409 14.4523 26.5577 14.4523C27.0248 14.4523 27.3985 14.8036 27.3985 15.2721V15.2955C27.3752 15.6234 27.1883 15.9279 26.8847 16.0685C26.2307 16.4432 25.5299 16.7712 24.0818 16.7712C21.4657 16.7712 19.8774 15.155 19.8774 12.4613C19.8774 9.2991 21.9796 8.15135 23.7547 8.15135C26.4175 8.15135 27.6321 10.2829 27.6321 12.2505C27.6555 12.7423 27.2584 13.164 26.7679 13.1874C26.7445 13.1874 26.7212 13.1874 26.6978 13.1874H21.7927C22.0496 14.3586 22.8905 14.991 24.2219 14.991Z" fill="#21C16C"/>
|
||||
<path d="M23.8018 9.81448C22.7741 9.79106 21.9098 10.564 21.8164 11.5947H25.8106C25.6938 10.564 24.8295 9.79106 23.8018 9.81448Z" fill="#21C16C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_6972_60099">
|
||||
<rect width="32" height="26" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>';
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user