Files
conservatorio-tomadini/plugins/system/nrframework/NRFramework/Integrations/MailChimp.php
2024-12-31 11:07:09 +01:00

511 lines
12 KiB
PHP

<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
use NRFramework\Functions;
// No direct access
defined('_JEXEC') or die;
class MailChimp extends Integration
{
/**
* MailChimp Endpoint URL
*
* @var string
*/
protected $endpoint = 'https://<dc>.api.mailchimp.com/3.0';
/**
* Create a new instance
*
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
if (strpos($this->key, '-') === false)
{
return;
}
list(, $data_center) = explode('-', $this->key);
$this->endpoint = str_replace('<dc>', $data_center, $this->endpoint);
$this->options->set('headers.Authorization', 'apikey ' . $this->key);
}
/**
* Subscribe user to MailChimp
*
* @param string $list_id The ID of the MailChimp list
* @param string $email The email address of the subscriber
* @param object $merge_fields The custom field that are associated with the subscriber where the keys are the merge tags.
* @param boolean $double_optin If true, the subscriber will be added with status "pending" and a confirmation email will be sent to the user.
* @param boolean $allow_update If true, the subscriber will be updated if it already exists. Otherwise, an error will be thrown.
* @param array $tags The tags that are associated with the subscriber.
* @param string $tags_replace Determines what changes to make to the subscriber's tags. Values: add_only, replace_all
* @param array $interests The interests that are associated with the subscriber.
* @param string $interests_replace Determines what changes to make to the subscriber's groups/interests. Values: add_only, replace_all
*
* @return void
*/
public function subscribeV2($list_id, $email, $merge_fields = null, $double_optin = true, $allow_update = true, $tags = null, $tags_replace = 'add_only', $interests = [], $interests_replace = 'add_only')
{
$data = [
'email_address' => $email,
'status' => $double_optin ? 'pending' : 'subscribed',
'merge_fields' => (object) $merge_fields,
'tags' => Functions::cleanArray($tags)
];
$member = $this->getMemberByEmail($list_id, $email);
// Prepare Interests
$interests = Functions::cleanArray($interests);
$interests = $interests ? array_fill_keys($interests, true) : [];
if ($member && isset($member['interests']) && $interests_replace == 'replace_all')
{
// Disable all existing groups
$memberInterests = array_fill_keys(array_keys($member['interests']), false);
// Merge new interests with existing interests
$interests = array_merge($memberInterests, $interests);
}
$data['interests'] = (object) $interests;
if (!$member)
{
// API Doc: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members
$this->post('lists/' . $list_id . '/members', $data);
return;
}
// Member exists
// Since member exists and we don't allow updating existing member, throw an error.
if (!$allow_update)
{
throw new \Exception('Member already exists');
}
// Skip double opt-in if the existing member is already confirmed
if (isset($member['status']) && $member['status'] == 'subscribed')
{
$data['status'] = $member['status'];
}
// Update existing member
// API Doc: https://mailchimp.com/developer/marketing/api/list-members/add-or-update-list-member
$this->put('lists/' . $list_id . '/members/' . $member['id'], $data);
// Remove existing member tags not included in the given Tags.
if ($member['tags'] && $tags_replace == 'replace_all')
{
$currentTags = array_map(function($item) { return $item['name']; }, $member['tags']);
if ($removeTags = array_diff($currentTags, $data['tags']))
{
$rTags = [];
foreach ($removeTags as $removeTag)
{
$rTags[] = [
'name' => $removeTag,
'status' => 'inactive'
];
}
// API Doc: https://mailchimp.com/developer/marketing/api/list-member-tags/add-or-remove-member-tags/
$this->post('lists/' . $list_id . '/members/' . $member['id'] . '/tags', ['tags' => $rTags]);
}
}
}
/**
* Subscribe user to MailChimp
*
* API References:
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#edit-put_lists_list_id_members_subscriber_hash
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members
*
* @param string $email User's email address
* @param string $list The MailChimp list unique ID
* @param Object $merge_fields Merge Fields
* @param boolean $update_existing Update existing user
* @param boolean $double_optin Send MailChimp confirmation email?
*
* @deprecated Use subscribeV2()
*
* @return void
*/
public function subscribe($email, $list, $merge_fields = array(), $update_existing = true, $double_optin = false)
{
$data = array(
'email_address' => $email,
'status' => $double_optin ? 'pending' : 'subscribed'
);
// add support for tags
if ($tags = $this->getTags($merge_fields))
{
$data['tags'] = $tags;
}
if (is_array($merge_fields) && count($merge_fields))
{
foreach ($merge_fields as $merge_field_key => $merge_field_value)
{
$value = is_array($merge_field_value) ? implode(',', $merge_field_value) : (string) $merge_field_value;
$data['merge_fields'][$merge_field_key] = $value;
}
}
$interests = $this->validateInterestCategories($list, $merge_fields);
if (!empty($interests))
{
$data = array_merge($data, array('interests' => $interests));
}
if ($update_existing)
{
// Get subscriber information.
$subscriberHash = md5(strtolower($email));
$member = $this->get('lists/' . $list . '/members/' . $subscriberHash);
// Skip double opt-in if the subscriber exists and it's confirmed
if (isset($member['status']) && $member['status'] == 'subscribed')
{
$data['status'] = $member['status'];
}
$this->put('lists/' . $list . '/members/' . $subscriberHash, $data);
if ($tags)
{
$tags_ = [];
foreach ($tags as $tag)
{
$tags_[] = [
'name' => $tag,
'status' => 'active'
];
}
$currentTags = $this->getMemberTags($list, $subscriberHash);
if ($removeTags = array_diff($currentTags, $tags))
{
foreach ($removeTags as $removeTag)
{
$tags_[] = [
'name' => $removeTag,
'status' => 'inactive'
];
}
}
$this->post('lists/' . $list . '/members/' . $subscriberHash . '/tags', ['tags' => $tags_]);
}
} else
{
$this->post('lists/' . $list . '/members', $data);
}
return true;
}
/**
* @deprecated Use subscribeV2()
*/
private function getMemberTags($list, $subscriberHash)
{
$tags = $this->get('lists/' . $list . '/members/' . $subscriberHash . '/tags');
$return = [];
if (isset($tags['tags']))
{
foreach ($tags['tags'] as $tag)
{
$return[] = $tag['name'];
}
}
return $return;
}
/**
* Find and return all unique tags
*
* @param array $merge_fields
*
* @deprecated use subscribeV2()
*
* @return array
*/
private function getTags($merge_fields)
{
$tags = [];
// ensure tags are added in the form
if (!isset($merge_fields['tags']))
{
return $tags;
}
$mergeFieldsTags = $merge_fields['tags'];
// make string array
if (is_string($mergeFieldsTags))
{
$tags = explode(',', $mergeFieldsTags);
}
// ensure we have array to manipulate
if (is_array($mergeFieldsTags) || is_object($mergeFieldsTags))
{
$tags = (array) $mergeFieldsTags;
}
// remove empty values, keep uniques and reset keys
$tags = array_filter($tags);
$tags = array_unique($tags);
$tags = array_values($tags);
$tags = array_map('trim', $tags);
return $tags;
}
/**
* Returns all available MailChimp lists
*
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/#read-get_lists
*
* @return array
*/
public function getLists()
{
$data = $this->get('/lists');
if (!$this->success())
{
return;
}
if (!isset($data['lists']) || !is_array($data['lists']))
{
return;
}
$lists = [];
foreach ($data['lists'] as $key => $list)
{
$lists[] = array(
'id' => $list['id'],
'name' => $list['name']
);
}
return $lists;
}
/**
* Gets the Interest Categories from MailChimp
*
* @param string $listID The List ID
*
* @deprecated Use subscribeV2()
*
* @return array
*/
public function getInterestCategories($listID)
{
if (!$listID)
{
return;
}
$data = $this->get('/lists/' . $listID . '/interest-categories');
if (!$this->success())
{
return;
}
if (isset($data['total_items']) && $data['total_items'] == 0)
{
return;
}
return $data['categories'];
}
/**
* Gets the values accepted for the particular Interest Category
*
* @param string $listID The List ID
* @param string $interestCategoryID The Interest Category ID
*
* @deprecated Use subscribeV2()
*
* @return array
*/
public function getInterestCategoryValues($listID, $interestCategoryID)
{
if (!$interestCategoryID || !$listID)
{
return array();
}
$data = $this->get('/lists/' . $listID . '/interest-categories/' . $interestCategoryID . '/interests');
if (isset($data['total_items']) && $data['total_items'] == 0)
{
return array();
}
return $data['interests'];
}
/**
* Filters the interests categories through the form fields
* and constructs the interests array for the subscribe method
*
* @param string $listID The List ID
* @param array $params The Form fields
*
* @deprecated Use subscribeV2()
*
* @return array
*/
public function validateInterestCategories($listID, $params)
{
if (!$params || !$listID)
{
return array();
}
$interestCategories = $this->getInterestCategories($listID);
if (!$interestCategories)
{
return array();
}
$categories = array();
foreach ($interestCategories as $category)
{
if (array_key_exists($category['title'], $params))
{
$categories[] = array('id' => $category['id'], 'title' => $category['title']);
}
}
if (empty($categories))
{
return array();
}
$interests = array();
foreach ($categories as $category)
{
$data = $this->getInterestCategoryValues($listID, $category['id']);
if (isset($data['total_items']) && $data['total_items'] == 0)
{
continue;
}
foreach ($data as $interest)
{
if (in_array($interest['name'], (array) $params[$category['title']]))
{
$interests[$interest['id']] = true;
}
else
{
$interests[$interest['id']] = false;
}
}
}
return $interests;
}
/**
* Find a subscriber in a list by email address
*
* @param string $list_id The MailChimp list ID
* @param string $email The email address
*
* @return mixed Object on success, false on failure
*/
public function getMemberByEmail($list_id, $email)
{
$subscriberHash = md5(strtolower($email));
$result = $this->get('lists/' . $list_id . '/members/' . $subscriberHash);
return $this->success() ? $result : false;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
if (isset($body['errors']))
{
$error = $body['errors'][0];
return $error['field'] . ': ' . $error['message'];
}
if (isset($body['detail']))
{
return $body['detail'];
}
}
/**
* The get() method overridden so that it handles
* the default item paging of MailChimp which is 10
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @return array|false Assoc array of API response, decoded from JSON
*/
public function get($method, $args = array())
{
$data = $this->makeRequest('get', $method, $args);
if ($data && isset($data['total_items']) && (int) $data['total_items'] > 10)
{
$args['count'] = $data['total_items'];
return $this->makeRequest('get', $method, $args);
}
return $data;
}
}