PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Subscribers / ImportExport / Import / MailChimp.php
mailpoet / lib / Subscribers / ImportExport / Import Last commit date
Cli.php 2 weeks ago Import.php 2 weeks ago MailChimp.php 2 months ago MailChimpDataMapper.php 2 months ago index.php 3 years ago
MailChimp.php
209 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Subscribers\ImportExport\Import;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Util\Helpers;
9
10 class MailChimp {
11 private const API_BASE_URI = 'https://user:%s@%s.api.mailchimp.com/3.0/';
12 private const API_KEY_REGEX = '/[a-zA-Z0-9]{32}-[a-zA-Z0-9]{2,4}$/';
13 private const API_BATCH_SIZE = 100;
14
15 /** @var false|string */
16 public $apiKey;
17 /** @var int */
18 public $maxPostSize;
19 /** @var false|string */
20 public $dataCenter;
21 /** @var MailChimpDataMapper */
22 private $mapper;
23
24 public function __construct(
25 string $apiKey
26 ) {
27 $this->apiKey = $this->getAPIKey($apiKey);
28 $this->maxPostSize = (int)Helpers::getMaxPostSize('bytes');
29 $this->dataCenter = $this->getDataCenter($this->apiKey);
30 $this->mapper = new MailChimpDataMapper();
31 }
32
33 public function getLists(): array {
34 if (!$this->apiKey || !$this->dataCenter) {
35 $this->throwException('API');
36 }
37
38 $lists = [];
39 $count = 0;
40 while (true) {
41 $data = $this->getApiData('lists', $count);
42 if ($data === null) {
43 $this->throwException('lists');
44 break;
45 }
46
47 $count += count($data['lists']);
48 foreach ($data['lists'] as $list) {
49 $lists[] = [
50 'id' => $list['id'],
51 'name' => $list['name'],
52 ];
53 }
54
55 if ($data['total_items'] <= $count) {
56 break;
57 }
58 }
59
60 return $lists;
61 }
62
63 public function getSubscribers(array $lists = []): array {
64 if (!$this->apiKey || !$this->dataCenter) {
65 $this->throwException('API');
66 }
67
68 if (!$lists) {
69 $this->throwException('lists');
70 }
71
72 $subscribers = [];
73 $duplicate = [];
74 $disallowed = [];
75 foreach ($lists as $list) {
76 $count = 0;
77 while (true) {
78 $data = $this->getApiData("lists/{$list}/members", $count);
79 if ($data === null) {
80 $this->throwException('lists');
81 break;
82 }
83 $count += count($data['members']);
84 foreach ($data['members'] as $member) {
85 $emailAddress = $member['email_address'];
86 if (!$this->isSubscriberAllowed($member)) {
87 $disallowed[$emailAddress] = $this->mapper->mapMember($member);
88 } elseif (isset($subscribers[$emailAddress])) {
89 $duplicate[$emailAddress] = $this->mapper->mapMember($member);
90 } else {
91 $subscribers[$emailAddress] = $this->mapper->mapMember($member);
92 }
93 }
94
95 if ($data['total_items'] <= $count) {
96 break;
97 }
98 }
99 }
100
101 if (!count($subscribers)) {
102 $this->throwException('subscribers');
103 }
104
105 return [
106 'subscribers' => array_values($subscribers),
107 'invalid' => [],
108 'duplicate' => $duplicate,
109 'disallowed' => $disallowed,
110 'role' => [],
111 'header' => $this->mapper->getMembersHeader(),
112 'subscribersCount' => count($subscribers),
113 ];
114 }
115
116 /**
117 * @param string|false $apiKey
118 * @return false|string
119 */
120 public function getDataCenter($apiKey) {
121 if (!$apiKey) return false;
122 $apiKeyParts = explode('-', $apiKey);
123 return end($apiKeyParts);
124 }
125
126 /**
127 * @param string $apiKey
128 * @return false|string
129 */
130 public function getAPIKey(string $apiKey) {
131 return (preg_match(self::API_KEY_REGEX, $apiKey)) ? $apiKey : false;
132 }
133
134 /**
135 * @param string $error
136 * @throws \Exception
137 */
138 public function throwException(string $error): void {
139 $errorMessage = __('Unknown MailChimp error.', 'mailpoet');
140 switch ($error) {
141 case 'API':
142 $errorMessage = __('Invalid API Key.', 'mailpoet');
143 break;
144 case 'size':
145 $errorMessage = __('The information received from MailChimp is too large for processing. Please limit the number of lists!', 'mailpoet');
146 break;
147 case 'subscribers':
148 $errorMessage = __('Did not find any active subscribers.', 'mailpoet');
149 break;
150 case 'lists':
151 $errorMessage = __('Did not find any valid lists.', 'mailpoet');
152 break;
153 }
154 throw new \Exception($errorMessage);
155 }
156
157 public function isSubscriberAllowed(array $subscriber): bool {
158 if (in_array($subscriber['status'], ['unsubscribed', 'cleaned', 'pending'], true)) {
159 return false;
160 }
161 if ($subscriber['member_rating'] < 2) {
162 return false;
163 }
164 // Rate 1 is on MailChimp API equal to 100% and we don't want to import avg_open_rate lower than 5%
165 if ($subscriber['stats']['avg_open_rate'] < 0.05) {
166 return false;
167 }
168 // We don't want to import avg_click_rate lower than 0.5%
169 if ($subscriber['stats']['avg_click_rate'] < 0.005) {
170 return false;
171 }
172
173 return true;
174 }
175
176 private function getApiData(string $endpoint, int $offset): ?array {
177 $url = sprintf(self::API_BASE_URI, $this->apiKey, $this->dataCenter);
178 $url .= $endpoint . '?' . http_build_query([
179 'count' => self::API_BATCH_SIZE,
180 'offset' => $offset,
181 ]);
182
183 $connection = @fopen($url, 'r');
184 if (!$connection) {
185 return null;
186 }
187
188 $bytesFetched = 0;
189 $response = '';
190 while (!feof($connection)) {
191 $buffer = fgets($connection, 4096);
192 if (!is_string($buffer)) {
193 return null;
194 }
195 if (trim($buffer) !== '') {
196 $response .= $buffer;
197 }
198 $bytesFetched += strlen((string)$buffer);
199 if ($bytesFetched > $this->maxPostSize) {
200 $this->throwException('size');
201 }
202 }
203 fclose($connection);
204
205 $decoded = json_decode($response, true);
206 return is_array($decoded) ? $decoded : null;
207 }
208 }
209