PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / trunk
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress vtrunk
11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 10.3.0 10.4.0 All 60 releases
acymailing / back / dynamics / acymailer / plugin.php

plugin.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress trunk, at back/dynamics/acymailer/plugin.php

1,303 lines 56.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use AcyMailing\Classes\MailClass;
4 use AcyMailing\Classes\QueueClass;
5 use AcyMailing\Core\AcymPlugin;
6
7 class plgAcymAcymailer extends AcymPlugin
8 {
9 const SENDING_METHOD_ID = 'acymailer';
10 const SENDING_METHOD_NAME = 'AcyMailing sending service';
11 const SENDING_METHOD_API_URL = 'https://api.acymailer.com/';
12 const CREDITS_RELOAD_DELAY = 900;
13 const TRANSLATIONS = [
14 0 => 'ACYM_ERROR_SEE_LOGS',
15 1 => 'ACYM_PLEASE_INSTALL_LATEST_VERSION',
16 2 => 'ACYM_EMAIL_SEND_FAIL_SEE_LOGS',
17 3 => 'ACYM_DOMAIN_NOT_ATTACHED_TO_THE_SITE',
18 4 => 'ACYM_NOT_ALLOWED_TO_DELETE_THIS_DOMAIN',
19 5 => 'ACYM_DOMAIN_DOES_NOT_EXIST',
20 6 => 'ACYM_DOMAIN_STATUS_SUCCESSFULLY_OBTAINED',
21 7 => 'ACYM_AN_ERROR_RELATED_TO_THE_REMAINING_CREDITS_OCCURRED',
22 8 => 'ACYM_LICENSE_EXPIRED',
23 9 => 'ACYM_IDENTITY_CREATED',
24 10 => 'ACYM_DOMAIN_ALREADY_EXISTS_FOR_THIS_WEBSITE',
25 11 => 'ACYM_DOMAIN_ALREADY_EXISTS_AND_DOES_NOT_BELONG_TO_YOU',
26 12 => 'ACYM_NO_DOMAINS_TO_CHECK',
27 13 => 'ACYM_DOMAINS_VERIFIED',
28 14 => 'ACYM_ERROR_WHILE_CHECKING_DOMAINS',
29 15 => 'ACYM_BLOCKED_LICENSE',
30 16 => 'ACYM_MISSING_EMAIL',
31 17 => 'ACYM_MISSING_ARGUMENTS',
32 18 => 'ACYM_NO_CREDITS_LEFT',
33 19 => 'ACYM_MISSING_API_KEY',
34 20 => 'ACYM_DOMAIN_NOT_FOUND',
35 21 => 'ACYM_LICENSE_NOT_FOUND',
36 22 => 'ACYM_EMAIL_SENT',
37 23 => 'ACYM_INVALID_API_KEY',
38 24 => 'ACYM_LICENSE_BLOCKED',
39 25 => 'ACYM_SEE_LOGS_FILE',
40 26 => 'ACYM_SERVICE_TEMPORARY_UNAVAILABLE',
41 27 => 'ACYM_DOMAIN_ONLY_ON_ONE_LICENSE',
42 28 => 'ACYM_EMAIL_UNDER_VERIFICATION',
43 29 => 'ACYM_DOMAIN_BLOCKED',
44 30 => 'ACYM_DOMAIN_UNDER_REVIEW',
45 ];
46
47 private array $errorCodes = [0, 1, 2, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30];
48 private array $cnameErrors = [];
49
50 public function __construct()
51 {
52 parent::__construct();
53 $this->pluginDescription->name = self::SENDING_METHOD_NAME;
54 }
55
56 protected function callApiSendingMethod(
57 string $url,
58 array $data = [],
59 array $headers = [],
60 string $type = 'GET',
61 array $authentication = [],
62 bool $dataDecoded = false,
63 array $files = []
64 ): array {
65 if (strpos($url, self::SENDING_METHOD_API_URL) === false) {
66 $url = self::SENDING_METHOD_API_URL.'api/'.$url;
67 }
68
69 $defaultHeaders = [
70 'Accept' => 'application/json',
71 'Content-Type' => 'application/json',
72 'API-KEY' => $this->config->get(self::SENDING_METHOD_ID.'_apikey', ''),
73 'Version' => $this->config->get('version'),
74 ];
75
76 $usedHeaders = array_map('strtolower', array_keys($headers));
77 foreach ($defaultHeaders as $key => $value) {
78 if (!in_array(strtolower($key), $usedHeaders, true)) {
79 $headers[$key] = $value;
80 }
81 }
82
83 $result = parent::callApiSendingMethod($url, $data, $headers, $type, $authentication, $dataDecoded, $files);
84 if (!empty($result['error_curl'])) {
85 $result['message'] = $result['error_curl'];
86 $this->errors[] = $result['error_curl'];
87 $this->errorCallback();
88 } else {
89 $result['message'] = $this->translate($result);
90 }
91
92 return $result;
93 }
94
95 public function onAcymGetSendingMethods(&$data, $isMailer = false)
96 {
97 $data['sendingMethods'][self::SENDING_METHOD_ID] = [
98 'name' => $this->pluginDescription->name,
99 'image' => ACYM_IMAGES.'mailers/'.self::SENDING_METHOD_ID.'.png',
100 'recommended' => true,
101 'image_class' => '',
102 ];
103 }
104
105 public function onAcymAttachedLicenseOption()
106 {
107 $licenseKey = $this->config->get('license_key');
108 $acyMailerKey = $this->config->get('acymailer_apikey');
109 $mailerMethod = $this->config->get('mailer_method');
110
111 if (empty($licenseKey) || empty($acyMailerKey) || $mailerMethod === self::SENDING_METHOD_ID) {
112 return;
113 }
114 ?>
115 <div class="grid-x grid-margin-x margin-top-1" id="activate_ass_method_notice">
116 <label class="cell medium-7 acym_vcenter">
117 <i class="acymicon-info-circle" id="acymailer_info_icon"></i>
118 <?php echo esc_html(acym_translation('ACYM_ACTIVATE_ACYMAILER')); ?>
119 </label>
120 <button type="button"
121 id="acym__configuration__activate__acymailer"
122 class="cell shrink button margin-bottom-0">
123 <?php echo esc_html(acym_translation('ACYM_SEE_THE_SENDING_METHOD')); ?>
124 </button>
125 </div>
126 <?php
127 }
128
129 public function onAcymGetSendingMethodsHtmlSetting(&$data)
130 {
131 $licenseKey = $this->config->get('license_key');
132 $acyMailerKey = $this->config->get('acymailer_apikey');
133 $domains = $this->config->get(self::SENDING_METHOD_ID.'_domains', []);
134 $domainsWaiting = [];
135 if (!empty($domains)) {
136 $domains = json_decode($domains, true);
137 $domainsWaiting = array_filter($domains, function ($domain) {
138 return $domain['status'] !== 'SUCCESS';
139 });
140 }
141
142 $unverifiedDomains = $this->getDomainsUnVerified();
143
144 ob_start();
145 ?>
146 <div class="send_settings cell grid-x acym_vcenter" id="<?php echo esc_attr(self::SENDING_METHOD_ID); ?>_settings">
147 <div class="cell grid-x acym__sending__methods__one__settings <?php echo empty($data['step']) ? 'xlarge-10 xxlarge-8' : ''; ?>">
148 <?php if (!acym_level(ACYM_ESSENTIAL) && empty($data['step'])) { ?>
149 <div class="acym_vcenter acym__config__acymailer__warning margin-top-1 cell grid-x large-8 xlarge-6 xxlarge-5">
150 <label for="acym__configuration__license-key" class="cell">
151 <?php echo esc_html(acym_translation('ACYM_YOUR_LICENSE_KEY')); ?>
152 </label>
153 <input type="text" name="config[license_key]" id="acym__configuration__license-key" class="cell auto" value="<?php echo esc_attr($licenseKey); ?>">
154 <button type="button"
155 id="acym__configuration__button__license"
156 class="cell shrink medium-3 button margin-top-1 medium-margin-top-0 medium-margin-left-1"
157 data-acym-linked="<?php echo empty($licenseKey) ? 0 : 1; ?>">
158 <?php echo esc_html(acym_translation(empty($licenseKey) ? 'ACYM_ATTACH_MY_LICENSE' : 'ACYM_UNLINK_MY_LICENSE')); ?>
159 </button>
160 </div>
161 <?php }
162 if (empty($licenseKey)) { ?>
163 <div class="cell acym_vcenter acym__config__acymailer__warning margin-top-1">
164 <i class="acymicon-exclamation-triangle acym__color__orange"></i>
165 <span class="margin-right-1">
166 <?php
167 $pricingPage = ACYM_ACYMAILING_WEBSITE.'pricing?utm_source=acymailing_plugin&utm_medium=sendingmethod&utm_campaign=purchase';
168 echo esc_html(acym_translation(acym_level(ACYM_ESSENTIAL) ? 'ACYM_NO_LICENSE' : 'ACYM_NO_LICENSE_STARTER'));
169 ?>
170 <a target="_blank" href="<?php echo esc_url($pricingPage); ?>"><?php echo esc_html(acym_translation('ACYM_GET_A_LICENSE')); ?></a>
171 </span>
172 </div>
173 <?php } elseif (empty($acyMailerKey)) { ?>
174 <div class="cell acym_vcenter acym__config__acymailer__warning margin-top-1">
175 <i class="acymicon-exclamation-triangle acym__color__orange"></i>
176 <span>
177 <?php
178 $subscriptionsPage = ACYM_ACYMAILING_WEBSITE.'account/license?utm_source=acymailing_plugin&utm_medium=sendingmethod&utm_campaign=purchase';
179 echo esc_html(acym_translation('ACYM_API_KEY_NOT_ALLOWING_METHOD'));
180 ?>
181 <a target="_blank" href="<?php echo esc_url($subscriptionsPage); ?>"><?php echo esc_html(
182 acym_translation('ACYM_SWITCH_MY_LICENSE')
183 ); ?></a>
184 </span>
185 </div>
186
187 <div class="acym__configuration__acymailer__check-license cell grid-x">
188 <div class="cell"><?php echo esc_html(acym_translation('ACYM_CHECK_SENDING_SERVICE_LICENSE')); ?></div>
189 <button
190 data-task="attachLicenseAcymailer"
191 id="acym__configuration__acymailer__verify-license"
192 class="cell medium-shrink button margin-left-0 margin-top-1 acy_button_submit">
193 <?php echo esc_html(acym_translation('ACYM_CHECK_LICENSE')); ?>
194 </button>
195 </div>
196 <?php } else { ?>
197 <div class="cell grid-x">
198 <?php if (!empty($domainsWaiting)) { ?>
199 <div class="cell <?php echo empty($data['step']) ? 'large-8' : ''; ?> grid-x acym_vcenter acym__config__acymailer__warning">
200 <i class="cell small-2 margin-right-0 acymicon-access-time acym__color__orange"></i>
201 <div class="cell small-10 grid-x">
202 <span class="cell"><?php echo esc_html(acym_translation('ACYM_DOMAINS_WAITING_VALIDATION_ADD_CNAME')); ?></span>
203 <a href="<?php echo esc_url(
204 ACYM_DOCUMENTATION
205 ); ?>external-sending-method/acymailing-sending-service#how-to-add-the-dns-entries-on-my-server"
206 target="_blank"
207 class="cell">
208 <?php echo esc_html(acym_translation('ACYM_CHECK_THIS_TUTORIAL')); ?>
209 </a>
210 </div>
211 </div>
212 <div class="cell <?php echo empty($data['step']) ? 'large-4' : ''; ?> margin-top-1 margin-bottom-1">
213 <button id="acym__config__acymailer__update-domain-status"
214 type="button"
215 class="button button-secondary float-right"
216 sending-method-id="<?php echo esc_attr(self::SENDING_METHOD_ID); ?>">
217 <i class="acymicon-autorenew"></i><?php echo esc_html(acym_translation('ACYM_UPDATE_DOMAIN_STATUS')); ?>
218 </button>
219 </div>
220 <?php } ?>
221 </div>
222 <div class="grid-x acym__listing cell">
223 <div class="grid-x cell acym__listing__header">
224 <div class="grid-x medium-auto small-11 cell acym__listing__header__title__container">
225 <div class="cell small-8 medium-auto acym__listing__header__title">
226 <?php echo esc_html(acym_translation('ACYM_DOMAIN_NAME')); ?>
227 </div>
228 <div class="cell hide-for-small-only medium-2 acym__listing__header__title text-center">
229 <?php echo esc_html(acym_translation('ACYM_STATUS')); ?>
230 </div>
231 <div class="cell hide-for-small-only medium-2 acym__listing__header__title text-center">
232 <?php echo esc_html(acym_translation('ACYM_BOUNCE_RATE'));
233 acym_info(['textShownInTooltip' => 'ACYM_BOUNCE_RATE_DESC']); ?>
234 </div>
235 <div class="cell hide-for-small-only medium-2 acym__listing__header__title text-center">
236 <?php echo esc_html(acym_translation('ACYM_COMPLAINT_RATE'));
237 acym_info(['textShownInTooltip' => 'ACYM_COMPLAINT_RATE_DESC']); ?>
238 </div>
239 <div class="cell small-4 medium-2 acym__listing__header__title text-center">
240 <?php echo esc_html(acym_translation('ACYM_ACTIONS')); ?>
241 </div>
242 </div>
243 </div>
244 <?php if (empty($domains)) { ?>
245 <div class="grid-x cell margin-top-1 align-center">
246 <?php echo esc_html(acym_translation('ACYM_NO_DOMAINS_YET')); ?>
247 </div>
248 <?php } else {
249 foreach ($domains as &$domain) {
250 $isValid = true;
251 foreach ($domain['CnameRecords'] as $i => $cnameRecord) {
252 $domain['CnameRecords'][$i]['isValid'] = $this->checkCnameEntry($cnameRecord['name'], $cnameRecord['value']);
253 $isValid = $isValid && $domain['CnameRecords'][$i]['isValid'];
254 $domain['CnameRecords'][$i]['tooltipMessages'] = $this->cnameErrors;
255 $this->cnameErrors = [];
256 }
257
258 if (!$isValid && $domain['status'] === 'SUCCESS') {
259 $domain['status'] = 'PENDING';
260 $this->config->saveConfig([self::SENDING_METHOD_ID.'_domains' => json_encode($domains)]);
261 }
262
263 $bounceRateColorClass = $this->getRateColor(
264 empty($domain['bounce_rate']) ? 0 : $domain['bounce_rate'],
265 empty($domain['allowed_bounce_rate']) ? 0 : $domain['allowed_bounce_rate'],
266 empty($domain['warning_bounce_rate']) ? 0 : $domain['warning_bounce_rate']
267 );
268 $complaintRateColorClass = $this->getRateColor(
269 empty($domain['complaint_rate']) ? 0 : $domain['complaint_rate'],
270 empty($domain['allowed_complaint_rate']) ? 0 : $domain['allowed_complaint_rate'],
271 empty($domain['warning_complaint_rate']) ? 0 : $domain['warning_complaint_rate']
272 );
273 ?>
274 <div acym-data-domain="<?php echo esc_attr($domain['domain']); ?>" class="grid-x cell align-middle acym__listing__row">
275 <div class="cell small-6 medium-auto grid-x acym__listing__title__container">
276 <?php echo esc_html($domain['domain']); ?>
277 </div>
278 <div class="cell small-1 medium-2 grid-x acym__listing__title__container align-center">
279 <?php
280 switch ($domain['status']) {
281 case 'SUCCESS':
282 $iconClass = 'acymicon-check-circle acym__color__green';
283 $tooltipText = acym_translation('ACYM_VALIDATED');
284 break;
285 case 'FAILED':
286 $iconClass = 'acymicon-close acym__color__red notValidated';
287 $tooltipText = acym_translation('ACYM_APPROVAL_FAILED');
288 break;
289 default:
290 $iconClass = 'acymicon-access-time acym__color__orange notValidated';
291 $tooltipText = acym_translation('ACYM_A_CNAME_MISSING');
292 }
293
294 acym_tooltip(
295 [
296 'hoveredText' => '<i class="acym__config__acymailer__status__icon '.$iconClass.'"></i>',
297 'textShownInTooltip' => $tooltipText,
298 ]
299 );
300 ?>
301 </div>
302 <div class="cell hide-for-small-only medium-2 grid-x acym__listing__title__container align-center <?php echo esc_attr(
303 $bounceRateColorClass
304 ); ?>">
305 <?php echo empty($domain['bounce_rate']) ? '-' : esc_html(round($domain['bounce_rate'], 2)).'%';
306 $this->displayRateIcon(
307 empty($domain['bounce_rate']) ? 0 : $domain['bounce_rate'],
308 empty($domain['allowed_bounce_rate']) ? 0 : $domain['allowed_bounce_rate'],
309 empty($domain['warning_bounce_rate']) ? 0 : $domain['warning_bounce_rate']
310 );
311 ?>
312 </div>
313 <div class="cell hide-for-small-only medium-2 grid-x acym__listing__title__container align-center <?php echo esc_attr(
314 $complaintRateColorClass
315 ); ?>">
316 <?php echo empty($domain['complaint_rate']) ? '-' : esc_html(round($domain['complaint_rate'], 2)).'%';
317 $this->displayRateIcon(
318 empty($domain['complaint_rate']) ? 0 : $domain['complaint_rate'],
319 empty($domain['allowed_complaint_rate']) ? 0 : $domain['allowed_complaint_rate'],
320 empty($domain['warning_complaint_rate']) ? 0 : $domain['warning_complaint_rate']
321 );
322 ?>
323 </div>
324 <div class="cell small-4 medium-2 grid-x margin-left-1 medium-margin-left-0 acym__listing__title__container align-center">
325 <?php ob_start(); ?>
326 <div class="cell grid-x acym__config__acymailer__cname__modal">
327 <h6 class="cell text-center margin-top-2 margin-bottom-1"><?php echo esc_html(acym_translation('ACYM_DNS_ENTRIES')); ?></h6>
328 <div class="grid-x cell align-center margin-top-1 margin-bottom-2 acym_vcenter">
329 <p class="cell shrink"><?php echo esc_html(acym_translation('ACYM_DO_NOT_KNOW_HOW_DO_IT')); ?> </p>
330 <a href="<?php echo esc_url(
331 ACYM_DOCUMENTATION
332 ); ?>external-sending-method/acymailing-sending-service#how-to-add-the-dns-entries-on-my-server"
333 class="cell shrink acym__config__acymailer__cname__modal__link"
334 target="_blank">
335 <?php echo esc_html(acym_translation('ACYM_STEP_BY_STEP_GUIDE')); ?>
336 </a>
337 </div>
338 <div class="grid-x acym__listing cell">
339 <div class="grid-x acym__listing cell">
340 <div class="cell grid-x grid-margin-x margin-left-0 padding-bottom-1 acym__listing__header">
341 <div class="cell small-10 hide-for-medium acym__listing__header__title">
342 <?php echo esc_html(acym_translation('ACYM_NAME').' / '.acym_translation('ACYM_VALUE')); ?>
343 </div>
344 <div class="cell small-5 show-for-medium acym__listing__header__title">
345 <?php echo esc_html(acym_translation('ACYM_NAME')); ?>
346 </div>
347 <div class="cell small-5 show-for-medium acym__listing__header__title">
348 <?php echo esc_html(acym_translation('ACYM_VALUE')); ?>
349 </div>
350 <div class="cell small-2 acym__listing__header__title text-center">
351 <?php echo esc_html(acym_translation('ACYM_STATUS')); ?>
352 </div>
353 </div>
354 <?php foreach ($domain['CnameRecords'] as $cnameRecord) { ?>
355 <div class="cell grid-x grid-margin-x margin-left-0 align-middle acym__listing__row">
356 <div class="cell small-11 medium-5 cname-name">
357 <?php echo esc_html($cnameRecord['name']); ?>
358 </div>
359 <div class="cell small-1 hide-for-medium cname-value">
360 <?php $this->displayCnameStatus($cnameRecord); ?>
361 </div>
362 <div class="cell small-11 medium-5 cname-value">
363 <?php echo esc_html($cnameRecord['value']); ?>
364 </div>
365 <div class="cell medium-2 show-for-medium text-center">
366 <?php $this->displayCnameStatus($cnameRecord); ?>
367 </div>
368 </div>
369 <?php } ?>
370 </div>
371 </div>
372 </div>
373 <?php
374 $modalContent = ob_get_clean();
375 ob_start();
376 acym_tooltip(
377 [
378 'hoveredText' => '<i class="acymicon-cogs acym__config__acymailer__domain--settings cursor-pointer acym__color__blue" acym-data-domain="'.esc_attr(
379 $domain['domain']
380 ).'"></i>',
381 'textShownInTooltip' => acym_translation('ACYM_SHOW_DNS_SETTINGS'),
382 ]
383 );
384 $buttonModal = ob_get_clean();
385 acym_modal(
386 $buttonModal,
387 $modalContent,
388 null,
389 [],
390 [],
391 true,
392 true,
393 'acym__config__acymailer__cname__modal__container'
394 );
395
396 acym_tooltip(
397 [
398 'hoveredText' => '<i class="acymicon-delete acym__config__acymailer__domain--delete cursor-pointer" acym-data-domain="'.esc_attr(
399 $domain['domain']
400 ).'"></i>',
401 'textShownInTooltip' => acym_translation('ACYM_DELETE'),
402 ]
403 );
404 ?>
405 </div>
406 </div>
407 <?php
408 }
409 }
410 ?>
411 </div>
412 <div class="cell grid-x acym_vcenter acym__sending__methods__one__settings padding-top-1">
413 <div class="cell grid-x">
414 <div class="cell small-7 medium-4 large-3 margin-next-1">
415 <input id="<?php echo esc_attr(self::SENDING_METHOD_ID); ?>_domain"
416 class="cell medium-6 large-4 xlarge-3"
417 type="text"
418 autocomplete="off"
419 value="">
420 <span id="<?php echo esc_attr(self::SENDING_METHOD_ID); ?>_domain_error" class="medium-6 large-4 xlarge-3"></span>
421 <?php
422 if (!empty($unverifiedDomains)) { ?>
423 <span id="acym__acymailer__unverifiedDomains">
424 <?php foreach ($unverifiedDomains as $oneDomain) { ?>
425 <span class="acym__acymailer__oneSuggestion"><?php echo esc_html($oneDomain); ?></span>
426 <?php } ?>
427 </span>
428 <?php } ?>
429 </div>
430 <div id="acym__configuration__sending__method_addDomain_submit" class="cell grid-x small-4 medium-6 large-8 acym_vcenter">
431 <button type="button"
432 id="acym__configuration__sending__method-addDomain"
433 class="cell shrink button button-secondary">
434 <?php echo esc_html(acym_translation('ACYM_ADD_A_DOMAIN')); ?>
435 </button>
436 <i class="margin-left-1 acym_vcenter acymicon-circle-o-notch acymicon-spin is-hidden"
437 id="acym__configuration__sending__method_add_domain-wait"></i>
438 <div class="cell shrink grid-x acym_vcenter" id="acym__configuration__acymailer__add__error">
439 <i class="acymicon-close acym__color__red cell shrink"></i>
440 <span class="cell shrink" id="acym__configuration__acymailer__add__error__message"></span>
441 </div>
442 </div>
443 </div>
444 </div>
445 <?php if (!$this->isLogFileEmpty()) { ?>
446 <div class="cell grid-x acym__sending__methods__log">
447 <?php
448 acym_modal(
449 acym_translation('ACYM_REPORT_SEE'),
450 '',
451 null,
452 [],
453 [
454 'class' => 'button',
455 'data-ajax' => 'true',
456 'data-iframe' => '&ctrl=configuration&task=seeLogs&filename='.$this->logFilename,
457 ]
458 );
459 ?>
460 </div>
461 <?php } ?>
462 <?php } ?>
463 </div>
464 </div>
465 <?php
466 $data['sendingMethodsHtmlSettings'][self::SENDING_METHOD_ID] = ob_get_clean();
467 }
468
469 public function onAcymTestCredentialSendingMethod($sendingMethod, $credentials)
470 {
471 if ($sendingMethod !== self::SENDING_METHOD_ID) return;
472
473 if (empty($credentials[self::SENDING_METHOD_ID.'_apikey'])) {
474 acym_sendAjaxResponse(acym_translation('ACYM_MISSING_API_KEY'), [], false);
475 }
476
477 $response = $this->callApiSendingMethod(
478 'get_credits',
479 [],
480 [
481 'API-KEY' => $credentials[self::SENDING_METHOD_ID.'_apikey'],
482 ]
483 );
484
485 if (empty($response)) {
486 $errorMsg = acym_translation('ACYM_NO_ANSWER');
487 acym_sendAjaxResponse(acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $errorMsg), [], false);
488 } elseif (!empty($response['error_curl'])) {
489 acym_sendAjaxResponse(acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $response['error_curl']), [], false);
490 } elseif (!empty($response['message']) && $response['message'] == 'Invalid private key') {
491 acym_sendAjaxResponse(acym_translation('ACYM_AUTHENTICATION_FAILS_WITH_API_KEY'), [], false);
492 } elseif (!empty($response['message']) && !$this->checkSuccessCode($this->responseCode)) {
493 if ($this->responseCode === 401) {
494 acym_sendAjaxResponse(
495 acym_translationSprintf(
496 'ACYM_SENDING_METHOD_ERROR_WHILE_ACTION',
497 self::SENDING_METHOD_NAME,
498 acym_translation('ACYM_GETTING_REMAINING_CREDITS'),
499 $response['message']
500 ),
501 [],
502 false
503 );
504 }
505 acym_sendAjaxResponse($response['message'], [], false);
506 } else {
507 acym_sendAjaxResponse(acym_translation('ACYM_API_KEY_CORRECT'));
508 }
509 }
510
511 public function onAcymSendEmail(&$response, $mailerHelper, $to, $from, $replyTo, $bcc = [], $attachments = [], $sendingMethodListParams = [])
512 {
513 if ($mailerHelper->externalMailer !== self::SENDING_METHOD_ID) {
514 return;
515 }
516
517 $apikey = $this->config->get(self::SENDING_METHOD_ID.'_apikey');
518 if (empty($apikey)) {
519 $mailerHelper->failedCounting = false;
520 $response['error'] = true;
521 $response['message'] = acym_translation('ACYM_MISSING_API_KEY');
522
523 return;
524 }
525
526 if ($this->isUnsubscribeLinkMissing($mailerHelper)) {
527 $mailerHelper->failedCounting = false;
528 $response['error'] = true;
529 $response['message'] = acym_translation('ACYM_MISSING_UNSUBSCRIBE_LINK');
530
531 return;
532 }
533
534 // In Amazon SES, you must verify each identity used as a "From", "Source", "Sender", or "Return-Path" address.
535 $domainsUsed = [
536 acym_getDomain($from['email']),
537 acym_getDomain($replyTo['email']),
538 ];
539 $bounceAddress = $this->getBounceAddress($mailerHelper);
540 if (!empty($bounceAddress)) {
541 $domainsUsed[] = acym_getDomain($bounceAddress);
542 }
543
544 $domainsUsed = array_unique($domainsUsed);
545
546 // Maybe don't try to send if one of these domains is not validated?
547 $verifiedDomains = $this->getVerifiedDomains();
548
549 $unverifiedDomains = array_diff($domainsUsed, $verifiedDomains);
550 if (!empty($unverifiedDomains)) {
551 $mailerHelper->failedCounting = false;
552 $response['error'] = true;
553 $response['message'] = acym_translationSprintf('ACYM_UNVERIFIED_DOMAINS_PREVENTING_EMAILS_FROM_BEING_SENT_X', implode(', ', $unverifiedDomains));
554
555 return;
556 }
557
558 $recipients = [];
559 if (!empty($mailerHelper->cc) || !empty($mailerHelper->bcc)) {
560 $recipients[] = $to['email'];
561 $recipients = array_merge($recipients, array_column($mailerHelper->cc, 0), array_column($mailerHelper->bcc, 0));
562 $mailerHelper->cc = [];
563 $mailerHelper->bcc = [];
564 }
565
566 $responseMailer = $this->callApiSendingMethod(
567 'send',
568 [
569 'email' => $mailerHelper->getSentMIMEMessage(),
570 'domainsUsed' => $domainsUsed,
571 'isTransactional' => $mailerHelper->isTransactional,
572 'isOneTimeMail' => $mailerHelper->isOneTimeMail,
573 'recipients' => $recipients,
574 ],
575 [],
576 'POST'
577 );
578
579 if (!empty($responseMailer['logs'])) {
580 acym_logError($responseMailer['logs'], self::SENDING_METHOD_ID);
581 }
582
583 if (!empty($responseMailer['error_curl'])) {
584 $mailerHelper->failedCounting = false;
585 $response['error'] = true;
586 $response['message'] = acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $responseMailer['error_curl']);
587 } elseif (!$this->checkSuccessCode($this->responseCode)) {
588 $response['error'] = true;
589 $response['message'] = $this->translate($responseMailer);
590
591 if ($response['message'] === acym_translation('ACYM_NO_CREDITS_LEFT')) {
592 $response['message'] = acym_translation('ACYM_NOT_ENOUGH_CREDITS').' : ';
593 $response['message'] .= '<a target="_blank" class="acym__color__blue" href="'.ACYM_ACYMAILING_WEBSITE.'">';
594 $response['message'] .= acym_translation('ACYM_GET_MORE_CREDITS');
595 $response['message'] .= '</a>';
596 }
597
598 // Don't unsubscribe/remove recipients if the error isn't related to them
599 if (!empty($responseMailer['code']) && in_array($responseMailer['code'], [18, 26, 28])) {
600 $mailerHelper->failedCounting = false;
601 }
602
603 if ($responseMailer['code'] == 26) {
604 $queueClass = new QueueClass();
605 $queueClass->delayAll(24);
606 }
607 } else {
608 $response['error'] = false;
609 }
610 }
611
612 public function replaceUserInformation(object &$email, ?object &$user, bool $send = true): void
613 {
614 if (empty($email->externalMailer) || $email->externalMailer !== self::SENDING_METHOD_ID) return;
615
616 $bounceAddress = $this->getBounceAddress($email);
617
618 if (!empty($bounceAddress) && method_exists($email, 'addCustomHeader')) {
619 $email->addCustomHeader('Return-Path', $bounceAddress);
620 }
621 }
622
623 public function ajaxCheckDomain()
624 {
625 $sendingMethod = acym_getVar('string', 'sendingMethod', '');
626 if ($sendingMethod != self::SENDING_METHOD_ID) {
627 acym_sendAjaxResponse();
628 }
629
630 $domains = json_decode($this->config->get(self::SENDING_METHOD_ID.'_domains', '[]'), true);
631 if (empty($domains)) {
632 acym_sendAjaxResponse();
633 }
634
635 $remainingDomains = $this->checkDomainsDNS($domains);
636 if (empty($remainingDomains)) {
637 acym_sendAjaxResponse('', ['domains' => $domains]);
638 }
639
640 $preparedDomains = array_map(
641 function ($domain) {
642 return $domain['domain'];
643 },
644 $remainingDomains
645 );
646
647 $responseApi = $this->callApiSendingMethod(
648 'getDomainStatus',
649 ['domains' => $preparedDomains],
650 [],
651 'POST'
652 );
653
654 if (empty($responseApi)) {
655 acym_sendAjaxResponse('', ['domains' => $domains]);
656 }
657
658 if (!$this->checkSuccessCode($this->responseCode)) {
659 acym_sendAjaxResponse(acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.$responseApi['message'], ['domains' => $domains], false);
660 }
661
662 $errorDomains = [];
663 foreach ($domains as $key => $oneDomain) {
664 $domainName = $oneDomain['domain'];
665 if (!array_key_exists($domainName, $responseApi['data']) || !is_array($responseApi['data'][$domainName])) {
666 $errorDomains[] = acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.$responseApi['data'][$domainName].': '.$domainName;
667 continue;
668 }
669
670 if (!isset($responseApi['data'][$domainName]['code'])) {
671 $domains[$key] = array_merge($domains[$key], $responseApi['data'][$domainName]);
672 continue;
673 }
674
675 if (!empty(self::TRANSLATIONS[$responseApi['data'][$domainName]['code']])) {
676 $message = self::TRANSLATIONS[$responseApi['data'][$domainName]['code']];
677 } else {
678 if (empty($responseApi['data'][$domainName]['message'])) {
679 $message = 'ACYM_DOMAIN_DEFAULT_ERROR';
680 } else {
681 $message = $this->displayMessage($responseApi['data'][$domainName]['message']);
682 }
683 }
684 acym_enqueueMessage(
685 acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.acym_translationSprintf(
686 $message,
687 $domainName
688 ),
689 'error'
690 );
691 }
692
693 $this->config->saveConfig([self::SENDING_METHOD_ID.'_domains' => json_encode($domains)]);
694
695 if (!empty($errorDomains)) {
696 acym_sendAjaxResponse(implode(', ', $errorDomains), [], false);
697 }
698
699 acym_sendAjaxResponse('', ['domains' => $domains]);
700 }
701
702 public function onAcymDeleteDomain()
703 {
704 $oneDomain = acym_getVar('string', 'oneDomain', '');
705
706 if (empty($oneDomain)) {
707 return;
708 }
709
710 $apikey = $this->config->get(self::SENDING_METHOD_ID.'_apikey');
711 if (empty($apikey)) {
712 acym_sendAjaxResponse(acym_translation('ACYM_MISSING_API_KEY'), [], false);
713 }
714
715 $responseApi = $this->callApiSendingMethod(
716 'deleteDomainIdentity',
717 ['domain' => $oneDomain, 'siteUrl' => acym_baseURI()],
718 [],
719 'POST'
720 );
721
722 if (!empty($responseApi['error_curl'])) {
723 $message = acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $responseApi['error_curl']);
724 acym_sendAjaxResponse($message, [], false);
725 } elseif (!empty($responseApi['message']) && $responseApi['message'] === 'Invalid private key') {
726 acym_sendAjaxResponse(acym_translation('ACYM_AUTHENTICATION_FAILS_WITH_API_KEY'), [], false);
727 } elseif (!$this->checkSuccessCode($this->responseCode) && (empty($responseApi['code']) || !in_array($responseApi['code'], [5, 4, 3]))) {
728 /*
729 We may ask the API for the deletion of a domain while:
730 - it already doesn't exist (error code 5)
731 - it doesn't belong to the current API key (error code 4)
732 - it isn't linked to this website (error code 3)
733
734 In all 3 cases, we don't want to keep this domain in the AcyMailing configuration, so we remove it and consider the deletion as a success
735 */
736
737 $message = acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.$this->translate($responseApi);
738 if (!empty($responseApi['logs'])) {
739 acym_logError($responseApi['logs'], self::SENDING_METHOD_ID);
740 }
741
742 acym_sendAjaxResponse($message, [], false);
743 } else {
744 $field = self::SENDING_METHOD_ID.'_domains';
745 $domains = json_decode($this->config->get($field, '[]'), true);
746 $domains = array_filter($domains, function ($domain) use ($oneDomain) {
747 return $domain['domain'] !== $oneDomain;
748 });
749
750 $this->config->saveConfig([$field => json_encode($domains)]);
751
752 acym_sendAjaxResponse();
753 }
754 }
755
756 public function ajaxAddDomain(): void
757 {
758 $oneDomain = acym_getVar('string', 'oneDomain', '');
759
760 $this->config->saveConfig(['mailer_method' => self::SENDING_METHOD_ID]);
761
762 if (empty($oneDomain)) {
763 acym_sendAjaxResponse(acym_translation('ACYM_MISSING_DOMAIN'), [], false);
764 }
765
766 $oneDomain = acym_strtolower($oneDomain);
767
768 $apikey = $this->config->get(self::SENDING_METHOD_ID.'_apikey');
769 if (empty($apikey)) {
770 acym_sendAjaxResponse(acym_translation('ACYM_MISSING_API_KEY'), [], false);
771 }
772
773 $domainValidation = preg_match('/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/u', $oneDomain);
774 if (!$domainValidation) {
775 $message = acym_translationSprintf('ACYM_THE_DOMAIN_X_IS_NOT_VALID', $oneDomain);
776 acym_sendAjaxResponse($message, ['type' => 'DOMAIN_VALIDATION'], false);
777 }
778
779 $field = self::SENDING_METHOD_ID.'_domains';
780 $domains = $this->config->get($field, []);
781
782 if (!empty($domains)) {
783 $domains = @json_decode($domains, true);
784 }
785
786 if (!empty($domains[$oneDomain])) {
787 acym_sendAjaxResponse(acym_translation('ACYM_DOMAIN_ALREADY_SENT'), [], false);
788 }
789
790 $responseApi = $this->callApiSendingMethod(
791 'createDomainIdentity',
792 ['domain' => $oneDomain, 'siteUrl' => acym_baseURI()],
793 [],
794 'POST'
795 );
796
797 if (!empty($responseApi['error_curl'])) {
798 $message = acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $responseApi['error_curl']);
799 acym_sendAjaxResponse($message, [], false);
800 } elseif (!empty($responseApi['message']) && $responseApi['message'] === 'Invalid API-KEY') {
801 acym_sendAjaxResponse(acym_translation('ACYM_AUTHENTICATION_FAILS_WITH_API_KEY'), [], false);
802 } elseif (!$this->checkSuccessCode($this->responseCode)) {
803 if (empty($responseApi['code']) || empty(self::TRANSLATIONS[$responseApi['code']])) {
804 $errorCodeTranslationKey = empty($responseApi['message']) ? 'ACYM_DOMAIN_DEFAULT_ERROR' : $this->displayMessage($responseApi['message']);
805 } else {
806 $errorCodeTranslationKey = self::TRANSLATIONS[$responseApi['code']];
807 }
808 $message = acym_translationSprintf($errorCodeTranslationKey, $oneDomain);
809 acym_sendAjaxResponse($message, [], false);
810 } elseif ($this->responseCode === 201) {
811 $domains[$oneDomain] = [
812 'domain' => $oneDomain,
813 'CnameRecords' => $responseApi['cnameRecords'],
814 'status' => 'PENDING',
815 ];
816 $this->config->saveConfig([$field => json_encode($domains)]);
817 acym_sendAjaxResponse($this->translate($responseApi), ['cnameRecords' => $responseApi['cnameRecords']]);
818 } else {
819 $cnameRecords = $responseApi['data'][$oneDomain]['cnameRecords'];
820 $status = $responseApi['data'][$oneDomain]['status'];
821 $domains[$oneDomain] = [
822 'domain' => $oneDomain,
823 'CnameRecords' => $cnameRecords,
824 'status' => $status,
825 ];
826 $this->config->saveConfig([$field => json_encode($domains)]);
827
828 acym_sendAjaxResponse(
829 $this->translate($responseApi),
830 ['cnameRecords' => $cnameRecords, 'alreadyVerified' => $status === 'SUCCESS']
831 );
832 }
833 }
834
835 public function onAcymDisplayPage()
836 {
837 if (intval($this->config->get('walk_through', 0)) === 1) {
838 return;
839 }
840
841 $unverifiedDomains = $this->getDomainsUnVerified();
842
843 if (!empty($unverifiedDomains)) {
844 $message = acym_translationSprintf('ACYM_UNVERIFIED_DOMAINS_PREVENTING_EMAILS_FROM_BEING_SENT_X', implode(', ', $unverifiedDomains));
845 $notification = [
846 'name' => 'unverified_domains',
847 'removable' => 0,
848 ];
849
850 if (acym_getVar('string', 'page') === 'acymailing_dashboard' || acym_getVar('string', 'ctrl') === 'dashboard') {
851 acym_enqueueMessage($message, 'warning', false, [$notification], false);
852 } else {
853 acym_enqueueMessage($message, 'warning', true, [$notification]);
854 }
855 } else {
856 acym_removeDashboardNotification('unverified_domains');
857 }
858 }
859
860 public function onAcymGetCreditRemainingSendingMethod(&$html, $reloading = false)
861 {
862 $sendingMethod = $this->config->get('mailer_method', '');
863 if (empty($sendingMethod) || $sendingMethod != self::SENDING_METHOD_ID) return;
864
865 $apiKey = $this->config->get(self::SENDING_METHOD_ID.'_apikey', '');
866 if (empty($apiKey)) return;
867
868 $response = $this->getRemainingCredits($reloading);
869
870 if (!isset($response['remaining_credits'])) {
871 if (empty($this->translate($response))) {
872 $html = acym_translation('ACYM_ERROR_CONTACT_ACYMAILING');
873 } else {
874 $html = acym_translationSprintf(
875 'ACYM_SENDING_METHOD_ERROR_WHILE_ACTION',
876 self::SENDING_METHOD_NAME,
877 acym_translation('ACYM_GETTING_REMAINING_CREDITS'),
878 $this->translate($response)
879 );
880 }
881
882 return;
883 }
884
885 $unityTranslation = 'ACYM_CREDITS_REMAINING';
886 $creditsShowed = $response['remaining_credits'];
887
888 if (!empty($response['is_multisite'])) {
889 $fromEmail = $this->config->get('from_email', '');
890 // If the license is multisite we check and increment credits on the domain of the from email
891 $domain = explode('@', $fromEmail)[1];
892
893 if (empty($domain) || empty($response['domains'][$domain])) {
894 $html = acym_translation('ACYM_DOMAIN_FROM_EMAIL_NOT_VALID_ASS');
895 } else {
896 $domainData = $response['domains'][$domain];
897 if ($domainData['is_limited']) {
898 $creditsShowed = $domainData['remaining_credits'];
899 } else {
900 $creditsShowed = $domainData['credits_used'];
901 $unityTranslation = 'ACYM_CREDITS_USED';
902 }
903 }
904 }
905
906 // Add security empty text message
907 if (empty($creditsShowed)) {
908 $creditsShowed = '0';
909 }
910
911 ob_start();
912 acym_tooltip(
913 [
914 'hoveredText' => $creditsShowed,
915 'textShownInTooltip' => acym_translation('ACYM_GET_MORE_CREDITS'),
916 'classContainer' => 'credits_remaining',
917 'link' => ACYM_ACYMAILING_WEBSITE,
918 ]
919 );
920 $tooltip = ob_get_clean();
921 $html = acym_translationSprintf(
922 'ACYM_SENDING_METHOD_X_UNITY',
923 self::SENDING_METHOD_NAME,
924 $tooltip,
925 '<span class="acym_not_bold">'.esc_html(acym_translation($unityTranslation)).'</span>'
926 );
927 }
928
929 /**
930 * Called before loading the emails of a batch to get the credits left
931 *
932 * @param int $creditsLeft
933 *
934 * @return void
935 */
936 public function onAcymCreditsLeft(&$creditsLeft)
937 {
938 $sendingMethod = $this->config->get('mailer_method', '');
939 if (empty($sendingMethod) || $sendingMethod != self::SENDING_METHOD_ID) return;
940
941 $apiKey = $this->config->get(self::SENDING_METHOD_ID.'_apikey', '');
942 if (empty($apiKey)) {
943 $creditsLeft = 0;
944
945 return;
946 }
947
948 $credits = $this->getRemainingCredits(true);
949 if (empty($credits['remaining_credits'])) {
950 $creditsLeft = 0;
951 } else {
952 $creditsLeft = (int)$credits['remaining_credits'];
953 }
954 }
955
956 public function onAcymAttachLicense($licenseKey)
957 {
958 if (empty($licenseKey)) {
959 return;
960 }
961
962 $response = $this->callApiSendingMethod(self::SENDING_METHOD_API_URL.'public/licenses/'.$licenseKey);
963
964 if (!empty($response['id'])) {
965 $this->config->saveConfig([self::SENDING_METHOD_ID.'_apikey' => $response['id']]);
966 }
967 }
968
969 public function onAcymDetachLicense()
970 {
971 $this->config->saveConfig([self::SENDING_METHOD_ID.'_apikey' => '']);
972 }
973
974 public function onAcymCampaignSummary(&$data)
975 {
976 if ($this->config->get('mailer_method') !== self::SENDING_METHOD_ID) {
977 return;
978 }
979
980 $mail = $data['mailClass']->getOneById($data['mailId']);
981 if (empty($mail->body) || !$this->isContainingUnsubscribeLink($mail->body)) {
982 $data['notAllowedSendingError'] = acym_translation('ACYM_UNSUB_LINK');
983 }
984
985 $unverifiedDomains = $this->getDomainsUnVerified();
986 if (!empty($unverifiedDomains)) {
987 $data['notAllowedSendingError'] = acym_translationSprintf(
988 'ACYM_UNVERIFIED_DOMAINS_PREVENTING_EMAILS_FROM_BEING_SENT_X',
989 implode(', ', $unverifiedDomains)
990 );
991 }
992 }
993
994 private function getDomainsUnVerified(): array
995 {
996 if ($this->config->get('mailer_method') !== self::SENDING_METHOD_ID) {
997 return [];
998 }
999
1000 // In Amazon SES, you must verify each identity used as a "From", "Source", "Sender", or "Return-Path" address.
1001 $allDomains = [];
1002
1003 $allDomains[] = acym_getDomain($this->config->get('from_email'));
1004 if ($this->config->get('from_as_replyto') === '0') {
1005 $allDomains[] = acym_getDomain($this->config->get('replyto_email'));
1006 }
1007
1008 if ($this->config->get('multilingual') === '1') {
1009 $senderInfoTranslation = $this->config->get('sender_info_translation');
1010 if (!empty($senderInfoTranslation)) {
1011 $senderInfoTranslation = json_decode($senderInfoTranslation, true);
1012 foreach ($senderInfoTranslation as $oneSenderInfo) {
1013 $allDomains[] = acym_getDomain($oneSenderInfo['from_email']);
1014 $allDomains[] = acym_getDomain($oneSenderInfo['replyto_email']);
1015 }
1016 }
1017 }
1018
1019 $allDomains[] = acym_getDomain($this->config->get('bounce_email'));
1020 $allDomains = array_filter(array_unique($allDomains));
1021
1022 $verifiedDomains = $this->getVerifiedDomains();
1023
1024 return array_diff($allDomains, $verifiedDomains);
1025 }
1026
1027 private function getRateColor(float $rate, float $allowedRate, float $warningRate): string
1028 {
1029 if (empty($rate) || empty($allowedRate) || empty($warningRate)) {
1030 return '';
1031 }
1032
1033 if ($rate > $allowedRate) {
1034 return 'acym__color__red';
1035 } elseif ($rate >= $warningRate) {
1036 return 'acym__color__orange';
1037 }
1038
1039 return '';
1040 }
1041
1042 private function displayRateIcon(float $rate, float $allowedRate, float $warningRate): void
1043 {
1044 if (empty($rate) || empty($allowedRate) || empty($warningRate)) {
1045 return;
1046 }
1047
1048 if ($rate > $allowedRate) {
1049 acym_tooltip(
1050 [
1051 'hoveredText' => '<i class="acym__config__acymailer__rate-status__icon acymicon-close acym__color__red"></i>',
1052 'textShownInTooltip' => acym_translation('ACYM_RATE_BLOCKED'),
1053 ]
1054 );
1055 } elseif ($rate >= $warningRate) {
1056 acym_tooltip(
1057 [
1058 'hoveredText' => '<i class="acym__config__acymailer__rate-status__icon acymicon-exclamation-triangle acym__color__orange"></i>',
1059 'textShownInTooltip' => acym_translation('ACYM_RATE_WARNING'),
1060 ]
1061 );
1062 }
1063 }
1064
1065 private function updateDomainRates($domainsRates)
1066 {
1067 $domains = $this->config->get(self::SENDING_METHOD_ID.'_domains', []);
1068
1069 if (empty($domains)) {
1070 return;
1071 }
1072
1073 $domains = @json_decode($domains, true);
1074
1075 foreach ($domainsRates as $oneDomain => $oneRate) {
1076 if (empty($domains[$oneDomain])) continue;
1077 foreach ($oneRate as $rateKey => $rateValue) {
1078 $domains[$oneDomain][$rateKey] = $rateValue;
1079 }
1080 }
1081
1082 $this->config->saveConfig([self::SENDING_METHOD_ID.'_domains' => json_encode($domains)]);
1083 }
1084
1085 private function getVerifiedDomains(): array
1086 {
1087 $verifiedDomains = [];
1088 $domainStatuses = json_decode($this->config->get(self::SENDING_METHOD_ID.'_domains', '[]'), true);
1089 if (!empty($domainStatuses)) {
1090 foreach ($domainStatuses as $domain => $info) {
1091 if ($info['status'] === 'SUCCESS') {
1092 $verifiedDomains[] = $domain;
1093 }
1094 }
1095 }
1096
1097 return $verifiedDomains;
1098 }
1099
1100 private function displayMessage($message): string
1101 {
1102 $correspondances = [
1103 'AlreadyExistsException' => 'ACYM_DOMAIN_X_ALREADY_EXIST',
1104 'TooManyRequestsException' => 'ACYM_DOMAIN_TO_MANY_REQUEST',
1105 'NotFoundException' => 'ACYM_DOMAIN_NOT_FOUND',
1106 'NotExistException' => 'ACYM_CANT_DELETE_DOMAIN',
1107 ];
1108
1109 if (empty($message) || empty($correspondances[$message])) {
1110 return 'ACYM_DOMAIN_DEFAULT_ERROR';
1111 }
1112
1113 return $correspondances[$message];
1114 }
1115
1116 private function getRemainingCredits($forceReload = false)
1117 {
1118 $time = time();
1119 $lastCheck = $this->config->get(self::SENDING_METHOD_ID.'_last_credits_check', 0);
1120 $lastDetails = $this->config->get(self::SENDING_METHOD_ID.'_credits_details', []);
1121
1122 if (!$forceReload && !empty($lastDetails) && $lastCheck > $time - self::CREDITS_RELOAD_DELAY) {
1123 return json_decode($lastDetails, true);
1124 }
1125
1126 $response = $this->callApiSendingMethod('get_credits');
1127
1128 $this->config->saveConfig([
1129 self::SENDING_METHOD_ID.'_credits_details' => json_encode($response),
1130 self::SENDING_METHOD_ID.'_last_credits_check' => $time,
1131 ]);
1132
1133 if (!empty($response['domains'])) {
1134 $this->updateDomainRates($response['domains']);
1135 }
1136
1137 return $response;
1138 }
1139
1140 private function checkSuccessCode($responseCode): bool
1141 {
1142 // we can add more success response codes if needed but for now, that's all the success response codes the API can send
1143 return in_array($responseCode, [200, 201]);
1144 }
1145
1146 private function translate($response)
1147 {
1148 if (!isset($response['code']) && empty($response['message'])) {
1149 return '';
1150 }
1151
1152 $correspondence = '';
1153 $messageData = [];
1154
1155
1156 if (isset($response['code']) && !empty(self::TRANSLATIONS[$response['code']])) {
1157 $correspondence = self::TRANSLATIONS[$response['code']];
1158 }
1159
1160 if (!empty($response['messageData'])) {
1161 $messageData = $response['messageData'];
1162 }
1163
1164 if (empty($correspondence)) {
1165 if (empty($messageData)) {
1166 return $response['message'];
1167 }
1168
1169 return acym_translationVsprintf($response['message'], $messageData, false);
1170 }
1171
1172 if (empty($messageData)) {
1173 $finalMessage = acym_translation($correspondence);
1174 } else {
1175 $finalMessage = acym_translationVsprintf($correspondence, $messageData);
1176 }
1177
1178 if (in_array($response['code'], $this->errorCodes)) {
1179 $this->errors[] = $finalMessage;
1180 $this->errorCallback();
1181 }
1182
1183 return $finalMessage;
1184 }
1185
1186 /**
1187 * Emails not needing an unsubscribe link:
1188 * - Sending a test from config, from campaign tab or from editor
1189 * - Forwarding a bounce message
1190 * - Sending a spam-test
1191 * - Overriding website emails
1192 * - Sending notification messages
1193 * - Unsubscribe emails
1194 * - Sending the walkthrough email
1195 * - Forwarding an email to a specific address from mailbox actions
1196 */
1197 private function isUnsubscribeLinkMissing($mailerHelper): bool
1198 {
1199 if ($mailerHelper->isTest || $mailerHelper->isForward || $mailerHelper->isSpamTest || empty($mailerHelper->mail)) {
1200 return false;
1201 }
1202
1203 if (in_array($mailerHelper->mail->type, MailClass::TYPES_TRANSACTIONAL) || $mailerHelper->mail->type === MailClass::TYPE_AUTOMATION) {
1204 return false;
1205 }
1206
1207 return !$this->isContainingUnsubscribeLink($mailerHelper->mail->body);
1208 }
1209
1210 private function isContainingUnsubscribeLink(string $text): bool
1211 {
1212 return strpos($text, '{unsubscribe}') !== false
1213 || strpos($text, '{unsubscribeall}') !== false
1214 || strpos($text, 'task=unsubscribe') !== false
1215 || strpos($text, 'frontusers/unsubscribe') !== false
1216 || strpos($text, '{direct_unsubscribe') !== false;
1217 }
1218
1219 private function checkDomainsDNS($domains): array
1220 {
1221 $remainingDomains = [];
1222 foreach ($domains as $oneDomain) {
1223 // No DNS entries yet, we have to check the domain to get DNS entries
1224 if (empty($oneDomain['CnameRecords'])) {
1225 $remainingDomains[] = $oneDomain;
1226 continue;
1227 }
1228
1229 // Check if all DNS entries are valid before calling the API
1230 foreach ($oneDomain['CnameRecords'] as $oneRecord) {
1231 if (!$this->checkCnameEntry($oneRecord['name'], $oneRecord['value'])) {
1232 $domainPosition = strpos($oneRecord['name'], '_domainkey') + 11;
1233 $domainName = substr($oneRecord['name'], $domainPosition);
1234 acym_sendAjaxResponse(acym_translation('ACYM_A_CNAME_MISSING'), ['domain' => $domainName], false);
1235 }
1236 }
1237
1238 $remainingDomains[] = $oneDomain;
1239 }
1240
1241 return $remainingDomains;
1242 }
1243
1244 private function checkCnameEntry(string $entryName, string $entryValue): bool
1245 {
1246 $domainPosition = strpos($entryName, '_domainkey') + 11;
1247 $domainName = substr($entryName, $domainPosition);
1248
1249 $dnsEntries = @dns_get_record($entryName, DNS_CNAME);
1250
1251 if (empty($dnsEntries)) {
1252 $entryName = substr($entryName, 0, $domainPosition);
1253 $doubleDomainEntry = $entryName.'.'.$domainName;
1254
1255 $dnsEntries = @dns_get_record($doubleDomainEntry, DNS_CNAME);
1256 if (!empty($dnsEntries)) {
1257 $this->cnameErrors[] = acym_translationSprintf('ACYM_ACYMAILER_MALFORMED_CNAME', $doubleDomainEntry);
1258 $this->cnameErrors[] = acym_translationSprintf('ACYM_ACYMAILER_CNAME_TRY_WITHOUT_DOMAIN', $entryName, $entryValue);
1259 } else {
1260 $this->cnameErrors[] = acym_translation('ACYM_WALK_ACYMAILER_STATUS_WAIT');
1261 }
1262
1263 return false;
1264 }
1265
1266 if (!is_array($dnsEntries)) {
1267 $dnsEntries = [$dnsEntries];
1268 }
1269
1270 foreach ($dnsEntries as $oneEntry) {
1271 if ($oneEntry['type'] !== 'CNAME' || $oneEntry['host'] !== $entryName) {
1272 continue;
1273 }
1274
1275 if (rtrim($oneEntry['target'], '.') === rtrim($entryValue, '.')) {
1276 return true;
1277 }
1278
1279 $this->cnameErrors[] = acym_translationSprintf('ACYM_DOMAIN_CHECK_INCORRECT_VALUE', $entryName, $domainName);
1280
1281 return false;
1282 }
1283
1284 $this->cnameErrors[] = acym_translation('ACYM_DNS_NOT_CNAME');
1285
1286 return false;
1287 }
1288
1289 private function displayCnameStatus(array $cnameRecord): void
1290 {
1291 if ($cnameRecord['isValid']) {
1292 echo '<i class="acym__config__acymailer__status__icon acymicon-check-circle acym__color__green"></i>';
1293 } else {
1294 acym_tooltip(
1295 [
1296 'hoveredText' => '<i class="acym__config__acymailer__status__icon acymicon-close acym__color__red"></i>',
1297 'textShownInTooltip' => implode('<br/>', $cnameRecord['tooltipMessages']),
1298 ]
1299 );
1300 }
1301 }
1302 }
1303