511 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			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;
 | |
| 	}
 | |
| } |