'ACYM_ERROR_SEE_LOGS',
1 => 'ACYM_PLEASE_INSTALL_LATEST_VERSION',
2 => 'ACYM_EMAIL_SEND_FAIL_SEE_LOGS',
3 => 'ACYM_DOMAIN_NOT_ATTACHED_TO_THE_SITE',
4 => 'ACYM_NOT_ALLOWED_TO_DELETE_THIS_DOMAIN',
5 => 'ACYM_DOMAIN_DOES_NOT_EXIST',
6 => 'ACYM_DOMAIN_STATUS_SUCCESSFULLY_OBTAINED',
7 => 'ACYM_AN_ERROR_RELATED_TO_THE_REMAINING_CREDITS_OCCURRED',
8 => 'ACYM_LICENSE_EXPIRED',
9 => 'ACYM_IDENTITY_CREATED',
10 => 'ACYM_DOMAIN_ALREADY_EXISTS_FOR_THIS_WEBSITE',
11 => 'ACYM_DOMAIN_ALREADY_EXISTS_AND_DOES_NOT_BELONG_TO_YOU',
12 => 'ACYM_NO_DOMAINS_TO_CHECK',
13 => 'ACYM_DOMAINS_VERIFIED',
14 => 'ACYM_ERROR_WHILE_CHECKING_DOMAINS',
15 => 'ACYM_BLOCKED_LICENSE',
16 => 'ACYM_MISSING_EMAIL',
17 => 'ACYM_MISSING_ARGUMENTS',
18 => 'ACYM_NO_CREDITS_LEFT',
19 => 'ACYM_MISSING_API_KEY',
20 => 'ACYM_DOMAIN_NOT_FOUND',
21 => 'ACYM_LICENSE_NOT_FOUND',
22 => 'ACYM_EMAIL_SENT',
23 => 'ACYM_INVALID_API_KEY',
24 => 'ACYM_LICENSE_BLOCKED',
25 => 'ACYM_SEE_LOGS_FILE',
26 => 'ACYM_SERVICE_TEMPORARY_UNAVAILABLE',
27 => 'ACYM_DOMAIN_ONLY_ON_ONE_LICENSE',
28 => 'ACYM_EMAIL_UNDER_VERIFICATION',
29 => 'ACYM_DOMAIN_BLOCKED',
30 => 'ACYM_DOMAIN_UNDER_REVIEW',
];
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];
private array $cnameErrors = [];
public function __construct()
{
parent::__construct();
$this->pluginDescription->name = self::SENDING_METHOD_NAME;
}
protected function callApiSendingMethod(
string $url,
array $data = [],
array $headers = [],
string $type = 'GET',
array $authentication = [],
bool $dataDecoded = false,
array $files = []
): array {
if (strpos($url, self::SENDING_METHOD_API_URL) === false) {
$url = self::SENDING_METHOD_API_URL.'api/'.$url;
}
$defaultHeaders = [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'API-KEY' => $this->config->get(self::SENDING_METHOD_ID.'_apikey', ''),
'Version' => $this->config->get('version'),
];
$usedHeaders = array_map('strtolower', array_keys($headers));
foreach ($defaultHeaders as $key => $value) {
if (!in_array(strtolower($key), $usedHeaders, true)) {
$headers[$key] = $value;
}
}
$result = parent::callApiSendingMethod($url, $data, $headers, $type, $authentication, $dataDecoded, $files);
if (!empty($result['error_curl'])) {
$result['message'] = $result['error_curl'];
$this->errors[] = $result['error_curl'];
$this->errorCallback();
} else {
$result['message'] = $this->translate($result);
}
return $result;
}
public function onAcymGetSendingMethods(&$data, $isMailer = false)
{
$data['sendingMethods'][self::SENDING_METHOD_ID] = [
'name' => $this->pluginDescription->name,
'image' => ACYM_IMAGES.'mailers/'.self::SENDING_METHOD_ID.'.png',
'recommended' => true,
'image_class' => '',
];
}
public function onAcymAttachedLicenseOption()
{
$licenseKey = $this->config->get('license_key');
$acyMailerKey = $this->config->get('acymailer_apikey');
$mailerMethod = $this->config->get('mailer_method');
if (empty($licenseKey) || empty($acyMailerKey) || $mailerMethod === self::SENDING_METHOD_ID) {
return;
}
?>
config->get('license_key');
$acyMailerKey = $this->config->get('acymailer_apikey');
$domains = $this->config->get(self::SENDING_METHOD_ID.'_domains', []);
$domainsWaiting = [];
if (!empty($domains)) {
$domains = json_decode($domains, true);
$domainsWaiting = array_filter($domains, function ($domain) {
return $domain['status'] !== 'SUCCESS';
});
}
$unverifiedDomains = $this->getDomainsUnVerified();
?>
$cnameRecord) {
$domain['CnameRecords'][$i]['isValid'] = $this->checkCnameEntry($cnameRecord['name'], $cnameRecord['value']);
$isValid = $isValid && $domain['CnameRecords'][$i]['isValid'];
$domain['CnameRecords'][$i]['tooltipMessages'] = $this->cnameErrors;
$this->cnameErrors = [];
}
if (!$isValid && $domain['status'] === 'SUCCESS') {
$domain['status'] = 'PENDING';
$this->config->saveConfig([self::SENDING_METHOD_ID.'_domains' => json_encode($domains)]);
}
$bounceRateColorClass = $this->getRateColor(
empty($domain['bounce_rate']) ? 0 : $domain['bounce_rate'],
empty($domain['allowed_bounce_rate']) ? 0 : $domain['allowed_bounce_rate'],
empty($domain['warning_bounce_rate']) ? 0 : $domain['warning_bounce_rate']
);
$complaintRateColorClass = $this->getRateColor(
empty($domain['complaint_rate']) ? 0 : $domain['complaint_rate'],
empty($domain['allowed_complaint_rate']) ? 0 : $domain['allowed_complaint_rate'],
empty($domain['warning_complaint_rate']) ? 0 : $domain['warning_complaint_rate']
);
?>
'',
'textShownInTooltip' => $tooltipText,
]
);
?>
displayRateIcon(
empty($domain['bounce_rate']) ? 0 : $domain['bounce_rate'],
empty($domain['allowed_bounce_rate']) ? 0 : $domain['allowed_bounce_rate'],
empty($domain['warning_bounce_rate']) ? 0 : $domain['warning_bounce_rate']
);
?>
displayRateIcon(
empty($domain['complaint_rate']) ? 0 : $domain['complaint_rate'],
empty($domain['allowed_complaint_rate']) ? 0 : $domain['allowed_complaint_rate'],
empty($domain['warning_complaint_rate']) ? 0 : $domain['warning_complaint_rate']
);
?>
displayCnameStatus($cnameRecord); ?>
displayCnameStatus($cnameRecord); ?>
'
',
'textShownInTooltip' => acym_translation('ACYM_SHOW_DNS_SETTINGS'),
]
);
$buttonModal = ob_get_clean();
acym_modal(
$buttonModal,
$modalContent,
null,
[],
[],
true,
true,
'acym__config__acymailer__cname__modal__container'
);
acym_tooltip(
[
'hoveredText' => '
',
'textShownInTooltip' => acym_translation('ACYM_DELETE'),
]
);
?>
isLogFileEmpty()) { ?>
'button',
'data-ajax' => 'true',
'data-iframe' => '&ctrl=configuration&task=seeLogs&filename='.$this->logFilename,
]
);
?>
callApiSendingMethod(
'get_credits',
[],
[
'API-KEY' => $credentials[self::SENDING_METHOD_ID.'_apikey'],
]
);
if (empty($response)) {
$errorMsg = acym_translation('ACYM_NO_ANSWER');
acym_sendAjaxResponse(acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $errorMsg), [], false);
} elseif (!empty($response['error_curl'])) {
acym_sendAjaxResponse(acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $response['error_curl']), [], false);
} elseif (!empty($response['message']) && $response['message'] == 'Invalid private key') {
acym_sendAjaxResponse(acym_translation('ACYM_AUTHENTICATION_FAILS_WITH_API_KEY'), [], false);
} elseif (!empty($response['message']) && !$this->checkSuccessCode($this->responseCode)) {
if ($this->responseCode === 401) {
acym_sendAjaxResponse(
acym_translationSprintf(
'ACYM_SENDING_METHOD_ERROR_WHILE_ACTION',
self::SENDING_METHOD_NAME,
acym_translation('ACYM_GETTING_REMAINING_CREDITS'),
$response['message']
),
[],
false
);
}
acym_sendAjaxResponse($response['message'], [], false);
} else {
acym_sendAjaxResponse(acym_translation('ACYM_API_KEY_CORRECT'));
}
}
public function onAcymSendEmail(&$response, $mailerHelper, $to, $from, $replyTo, $bcc = [], $attachments = [], $sendingMethodListParams = [])
{
if ($mailerHelper->externalMailer !== self::SENDING_METHOD_ID) {
return;
}
$apikey = $this->config->get(self::SENDING_METHOD_ID.'_apikey');
if (empty($apikey)) {
$mailerHelper->failedCounting = false;
$response['error'] = true;
$response['message'] = acym_translation('ACYM_MISSING_API_KEY');
return;
}
if ($this->isUnsubscribeLinkMissing($mailerHelper)) {
$mailerHelper->failedCounting = false;
$response['error'] = true;
$response['message'] = acym_translation('ACYM_MISSING_UNSUBSCRIBE_LINK');
return;
}
// In Amazon SES, you must verify each identity used as a "From", "Source", "Sender", or "Return-Path" address.
$domainsUsed = [
acym_getDomain($from['email']),
acym_getDomain($replyTo['email']),
];
$bounceAddress = $this->getBounceAddress($mailerHelper);
if (!empty($bounceAddress)) {
$domainsUsed[] = acym_getDomain($bounceAddress);
}
$domainsUsed = array_unique($domainsUsed);
// Maybe don't try to send if one of these domains is not validated?
$verifiedDomains = $this->getVerifiedDomains();
$unverifiedDomains = array_diff($domainsUsed, $verifiedDomains);
if (!empty($unverifiedDomains)) {
$mailerHelper->failedCounting = false;
$response['error'] = true;
$response['message'] = acym_translationSprintf('ACYM_UNVERIFIED_DOMAINS_PREVENTING_EMAILS_FROM_BEING_SENT_X', implode(', ', $unverifiedDomains));
return;
}
$recipients = [];
if (!empty($mailerHelper->cc) || !empty($mailerHelper->bcc)) {
$recipients[] = $to['email'];
$recipients = array_merge($recipients, array_column($mailerHelper->cc, 0), array_column($mailerHelper->bcc, 0));
$mailerHelper->cc = [];
$mailerHelper->bcc = [];
}
$responseMailer = $this->callApiSendingMethod(
'send',
[
'email' => $mailerHelper->getSentMIMEMessage(),
'domainsUsed' => $domainsUsed,
'isTransactional' => $mailerHelper->isTransactional,
'isOneTimeMail' => $mailerHelper->isOneTimeMail,
'recipients' => $recipients,
],
[],
'POST'
);
if (!empty($responseMailer['logs'])) {
acym_logError($responseMailer['logs'], self::SENDING_METHOD_ID);
}
if (!empty($responseMailer['error_curl'])) {
$mailerHelper->failedCounting = false;
$response['error'] = true;
$response['message'] = acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $responseMailer['error_curl']);
} elseif (!$this->checkSuccessCode($this->responseCode)) {
$response['error'] = true;
$response['message'] = $this->translate($responseMailer);
if ($response['message'] === acym_translation('ACYM_NO_CREDITS_LEFT')) {
$response['message'] = acym_translation('ACYM_NOT_ENOUGH_CREDITS').' : ';
$response['message'] .= '';
$response['message'] .= acym_translation('ACYM_GET_MORE_CREDITS');
$response['message'] .= '';
}
// Don't unsubscribe/remove recipients if the error isn't related to them
if (!empty($responseMailer['code']) && in_array($responseMailer['code'], [18, 26, 28])) {
$mailerHelper->failedCounting = false;
}
if ($responseMailer['code'] == 26) {
$queueClass = new QueueClass();
$queueClass->delayAll(24);
}
} else {
$response['error'] = false;
}
}
public function replaceUserInformation(object &$email, ?object &$user, bool $send = true): void
{
if (empty($email->externalMailer) || $email->externalMailer !== self::SENDING_METHOD_ID) return;
$bounceAddress = $this->getBounceAddress($email);
if (!empty($bounceAddress) && method_exists($email, 'addCustomHeader')) {
$email->addCustomHeader('Return-Path', $bounceAddress);
}
}
public function ajaxCheckDomain()
{
$sendingMethod = acym_getVar('string', 'sendingMethod', '');
if ($sendingMethod != self::SENDING_METHOD_ID) {
acym_sendAjaxResponse();
}
$domains = json_decode($this->config->get(self::SENDING_METHOD_ID.'_domains', '[]'), true);
if (empty($domains)) {
acym_sendAjaxResponse();
}
$remainingDomains = $this->checkDomainsDNS($domains);
if (empty($remainingDomains)) {
acym_sendAjaxResponse('', ['domains' => $domains]);
}
$preparedDomains = array_map(
function ($domain) {
return $domain['domain'];
},
$remainingDomains
);
$responseApi = $this->callApiSendingMethod(
'getDomainStatus',
['domains' => $preparedDomains],
[],
'POST'
);
if (empty($responseApi)) {
acym_sendAjaxResponse('', ['domains' => $domains]);
}
if (!$this->checkSuccessCode($this->responseCode)) {
acym_sendAjaxResponse(acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.$responseApi['message'], ['domains' => $domains], false);
}
$errorDomains = [];
foreach ($domains as $key => $oneDomain) {
$domainName = $oneDomain['domain'];
if (!array_key_exists($domainName, $responseApi['data']) || !is_array($responseApi['data'][$domainName])) {
$errorDomains[] = acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.$responseApi['data'][$domainName].': '.$domainName;
continue;
}
if (!isset($responseApi['data'][$domainName]['code'])) {
$domains[$key] = array_merge($domains[$key], $responseApi['data'][$domainName]);
continue;
}
if (!empty(self::TRANSLATIONS[$responseApi['data'][$domainName]['code']])) {
$message = self::TRANSLATIONS[$responseApi['data'][$domainName]['code']];
} else {
if (empty($responseApi['data'][$domainName]['message'])) {
$message = 'ACYM_DOMAIN_DEFAULT_ERROR';
} else {
$message = $this->displayMessage($responseApi['data'][$domainName]['message']);
}
}
acym_enqueueMessage(
acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.acym_translationSprintf(
$message,
$domainName
),
'error'
);
}
$this->config->saveConfig([self::SENDING_METHOD_ID.'_domains' => json_encode($domains)]);
if (!empty($errorDomains)) {
acym_sendAjaxResponse(implode(', ', $errorDomains), [], false);
}
acym_sendAjaxResponse('', ['domains' => $domains]);
}
public function onAcymDeleteDomain()
{
$oneDomain = acym_getVar('string', 'oneDomain', '');
if (empty($oneDomain)) {
return;
}
$apikey = $this->config->get(self::SENDING_METHOD_ID.'_apikey');
if (empty($apikey)) {
acym_sendAjaxResponse(acym_translation('ACYM_MISSING_API_KEY'), [], false);
}
$responseApi = $this->callApiSendingMethod(
'deleteDomainIdentity',
['domain' => $oneDomain, 'siteUrl' => acym_baseURI()],
[],
'POST'
);
if (!empty($responseApi['error_curl'])) {
$message = acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $responseApi['error_curl']);
acym_sendAjaxResponse($message, [], false);
} elseif (!empty($responseApi['message']) && $responseApi['message'] === 'Invalid private key') {
acym_sendAjaxResponse(acym_translation('ACYM_AUTHENTICATION_FAILS_WITH_API_KEY'), [], false);
} elseif (!$this->checkSuccessCode($this->responseCode) && (empty($responseApi['code']) || !in_array($responseApi['code'], [5, 4, 3]))) {
/*
We may ask the API for the deletion of a domain while:
- it already doesn't exist (error code 5)
- it doesn't belong to the current API key (error code 4)
- it isn't linked to this website (error code 3)
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
*/
$message = acym_translation('ACYM_ERROR_ON_CALL_ACYBA_WEBSITE').': '.$this->translate($responseApi);
if (!empty($responseApi['logs'])) {
acym_logError($responseApi['logs'], self::SENDING_METHOD_ID);
}
acym_sendAjaxResponse($message, [], false);
} else {
$field = self::SENDING_METHOD_ID.'_domains';
$domains = json_decode($this->config->get($field, '[]'), true);
$domains = array_filter($domains, function ($domain) use ($oneDomain) {
return $domain['domain'] !== $oneDomain;
});
$this->config->saveConfig([$field => json_encode($domains)]);
acym_sendAjaxResponse();
}
}
public function ajaxAddDomain(): void
{
$oneDomain = acym_getVar('string', 'oneDomain', '');
$this->config->saveConfig(['mailer_method' => self::SENDING_METHOD_ID]);
if (empty($oneDomain)) {
acym_sendAjaxResponse(acym_translation('ACYM_MISSING_DOMAIN'), [], false);
}
$oneDomain = acym_strtolower($oneDomain);
$apikey = $this->config->get(self::SENDING_METHOD_ID.'_apikey');
if (empty($apikey)) {
acym_sendAjaxResponse(acym_translation('ACYM_MISSING_API_KEY'), [], false);
}
$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);
if (!$domainValidation) {
$message = acym_translationSprintf('ACYM_THE_DOMAIN_X_IS_NOT_VALID', $oneDomain);
acym_sendAjaxResponse($message, ['type' => 'DOMAIN_VALIDATION'], false);
}
$field = self::SENDING_METHOD_ID.'_domains';
$domains = $this->config->get($field, []);
if (!empty($domains)) {
$domains = @json_decode($domains, true);
}
if (!empty($domains[$oneDomain])) {
acym_sendAjaxResponse(acym_translation('ACYM_DOMAIN_ALREADY_SENT'), [], false);
}
$responseApi = $this->callApiSendingMethod(
'createDomainIdentity',
['domain' => $oneDomain, 'siteUrl' => acym_baseURI()],
[],
'POST'
);
if (!empty($responseApi['error_curl'])) {
$message = acym_translationSprintf('ACYM_ERROR_OCCURRED_WHILE_CALLING_API', $responseApi['error_curl']);
acym_sendAjaxResponse($message, [], false);
} elseif (!empty($responseApi['message']) && $responseApi['message'] === 'Invalid API-KEY') {
acym_sendAjaxResponse(acym_translation('ACYM_AUTHENTICATION_FAILS_WITH_API_KEY'), [], false);
} elseif (!$this->checkSuccessCode($this->responseCode)) {
if (empty($responseApi['code']) || empty(self::TRANSLATIONS[$responseApi['code']])) {
$errorCodeTranslationKey = empty($responseApi['message']) ? 'ACYM_DOMAIN_DEFAULT_ERROR' : $this->displayMessage($responseApi['message']);
} else {
$errorCodeTranslationKey = self::TRANSLATIONS[$responseApi['code']];
}
$message = acym_translationSprintf($errorCodeTranslationKey, $oneDomain);
acym_sendAjaxResponse($message, [], false);
} elseif ($this->responseCode === 201) {
$domains[$oneDomain] = [
'domain' => $oneDomain,
'CnameRecords' => $responseApi['cnameRecords'],
'status' => 'PENDING',
];
$this->config->saveConfig([$field => json_encode($domains)]);
acym_sendAjaxResponse($this->translate($responseApi), ['cnameRecords' => $responseApi['cnameRecords']]);
} else {
$cnameRecords = $responseApi['data'][$oneDomain]['cnameRecords'];
$status = $responseApi['data'][$oneDomain]['status'];
$domains[$oneDomain] = [
'domain' => $oneDomain,
'CnameRecords' => $cnameRecords,
'status' => $status,
];
$this->config->saveConfig([$field => json_encode($domains)]);
acym_sendAjaxResponse(
$this->translate($responseApi),
['cnameRecords' => $cnameRecords, 'alreadyVerified' => $status === 'SUCCESS']
);
}
}
public function onAcymDisplayPage()
{
if (intval($this->config->get('walk_through', 0)) === 1) {
return;
}
$unverifiedDomains = $this->getDomainsUnVerified();
if (!empty($unverifiedDomains)) {
$message = acym_translationSprintf('ACYM_UNVERIFIED_DOMAINS_PREVENTING_EMAILS_FROM_BEING_SENT_X', implode(', ', $unverifiedDomains));
$notification = [
'name' => 'unverified_domains',
'removable' => 0,
];
if (acym_getVar('string', 'page') === 'acymailing_dashboard' || acym_getVar('string', 'ctrl') === 'dashboard') {
acym_enqueueMessage($message, 'warning', false, [$notification], false);
} else {
acym_enqueueMessage($message, 'warning', true, [$notification]);
}
} else {
acym_removeDashboardNotification('unverified_domains');
}
}
public function onAcymGetCreditRemainingSendingMethod(&$html, $reloading = false)
{
$sendingMethod = $this->config->get('mailer_method', '');
if (empty($sendingMethod) || $sendingMethod != self::SENDING_METHOD_ID) return;
$apiKey = $this->config->get(self::SENDING_METHOD_ID.'_apikey', '');
if (empty($apiKey)) return;
$response = $this->getRemainingCredits($reloading);
if (!isset($response['remaining_credits'])) {
if (empty($this->translate($response))) {
$html = acym_translation('ACYM_ERROR_CONTACT_ACYMAILING');
} else {
$html = acym_translationSprintf(
'ACYM_SENDING_METHOD_ERROR_WHILE_ACTION',
self::SENDING_METHOD_NAME,
acym_translation('ACYM_GETTING_REMAINING_CREDITS'),
$this->translate($response)
);
}
return;
}
$unityTranslation = 'ACYM_CREDITS_REMAINING';
$creditsShowed = $response['remaining_credits'];
if (!empty($response['is_multisite'])) {
$fromEmail = $this->config->get('from_email', '');
// If the license is multisite we check and increment credits on the domain of the from email
$domain = explode('@', $fromEmail)[1];
if (empty($domain) || empty($response['domains'][$domain])) {
$html = acym_translation('ACYM_DOMAIN_FROM_EMAIL_NOT_VALID_ASS');
} else {
$domainData = $response['domains'][$domain];
if ($domainData['is_limited']) {
$creditsShowed = $domainData['remaining_credits'];
} else {
$creditsShowed = $domainData['credits_used'];
$unityTranslation = 'ACYM_CREDITS_USED';
}
}
}
// Add security empty text message
if (empty($creditsShowed)) {
$creditsShowed = '0';
}
ob_start();
acym_tooltip(
[
'hoveredText' => $creditsShowed,
'textShownInTooltip' => acym_translation('ACYM_GET_MORE_CREDITS'),
'classContainer' => 'credits_remaining',
'link' => ACYM_ACYMAILING_WEBSITE,
]
);
$tooltip = ob_get_clean();
$html = acym_translationSprintf(
'ACYM_SENDING_METHOD_X_UNITY',
self::SENDING_METHOD_NAME,
$tooltip,
''.esc_html(acym_translation($unityTranslation)).''
);
}
/**
* Called before loading the emails of a batch to get the credits left
*
* @param int $creditsLeft
*
* @return void
*/
public function onAcymCreditsLeft(&$creditsLeft)
{
$sendingMethod = $this->config->get('mailer_method', '');
if (empty($sendingMethod) || $sendingMethod != self::SENDING_METHOD_ID) return;
$apiKey = $this->config->get(self::SENDING_METHOD_ID.'_apikey', '');
if (empty($apiKey)) {
$creditsLeft = 0;
return;
}
$credits = $this->getRemainingCredits(true);
if (empty($credits['remaining_credits'])) {
$creditsLeft = 0;
} else {
$creditsLeft = (int)$credits['remaining_credits'];
}
}
public function onAcymAttachLicense($licenseKey)
{
if (empty($licenseKey)) {
return;
}
$response = $this->callApiSendingMethod(self::SENDING_METHOD_API_URL.'public/licenses/'.$licenseKey);
if (!empty($response['id'])) {
$this->config->saveConfig([self::SENDING_METHOD_ID.'_apikey' => $response['id']]);
}
}
public function onAcymDetachLicense()
{
$this->config->saveConfig([self::SENDING_METHOD_ID.'_apikey' => '']);
}
public function onAcymCampaignSummary(&$data)
{
if ($this->config->get('mailer_method') !== self::SENDING_METHOD_ID) {
return;
}
$mail = $data['mailClass']->getOneById($data['mailId']);
if (empty($mail->body) || !$this->isContainingUnsubscribeLink($mail->body)) {
$data['notAllowedSendingError'] = acym_translation('ACYM_UNSUB_LINK');
}
$unverifiedDomains = $this->getDomainsUnVerified();
if (!empty($unverifiedDomains)) {
$data['notAllowedSendingError'] = acym_translationSprintf(
'ACYM_UNVERIFIED_DOMAINS_PREVENTING_EMAILS_FROM_BEING_SENT_X',
implode(', ', $unverifiedDomains)
);
}
}
public function onAcymSendingMethodOptions(&$data)
{
$data['embedImage'][self::SENDING_METHOD_ID] = false;
$data['embedAttachment'][self::SENDING_METHOD_ID] = false;
}
private function getDomainsUnVerified(): array
{
if ($this->config->get('mailer_method') !== self::SENDING_METHOD_ID) {
return [];
}
// In Amazon SES, you must verify each identity used as a "From", "Source", "Sender", or "Return-Path" address.
$allDomains = [];
$allDomains[] = acym_getDomain($this->config->get('from_email'));
if ($this->config->get('from_as_replyto') === '0') {
$allDomains[] = acym_getDomain($this->config->get('replyto_email'));
}
if ($this->config->get('multilingual') === '1') {
$senderInfoTranslation = $this->config->get('sender_info_translation');
if (!empty($senderInfoTranslation)) {
$senderInfoTranslation = json_decode($senderInfoTranslation, true);
foreach ($senderInfoTranslation as $oneSenderInfo) {
$allDomains[] = acym_getDomain($oneSenderInfo['from_email']);
$allDomains[] = acym_getDomain($oneSenderInfo['replyto_email']);
}
}
}
$allDomains[] = acym_getDomain($this->config->get('bounce_email'));
$allDomains = array_filter(array_unique($allDomains));
$verifiedDomains = $this->getVerifiedDomains();
return array_diff($allDomains, $verifiedDomains);
}
private function getRateColor(float $rate, float $allowedRate, float $warningRate): string
{
if (empty($rate) || empty($allowedRate) || empty($warningRate)) {
return '';
}
if ($rate > $allowedRate) {
return 'acym__color__red';
} elseif ($rate >= $warningRate) {
return 'acym__color__orange';
}
return '';
}
private function displayRateIcon(float $rate, float $allowedRate, float $warningRate): void
{
if (empty($rate) || empty($allowedRate) || empty($warningRate)) {
return;
}
if ($rate > $allowedRate) {
acym_tooltip(
[
'hoveredText' => '',
'textShownInTooltip' => acym_translation('ACYM_RATE_BLOCKED'),
]
);
} elseif ($rate >= $warningRate) {
acym_tooltip(
[
'hoveredText' => '',
'textShownInTooltip' => acym_translation('ACYM_RATE_WARNING'),
]
);
}
}
private function updateDomainRates($domainsRates)
{
$domains = $this->config->get(self::SENDING_METHOD_ID.'_domains', []);
if (empty($domains)) {
return;
}
$domains = @json_decode($domains, true);
foreach ($domainsRates as $oneDomain => $oneRate) {
if (empty($domains[$oneDomain])) continue;
foreach ($oneRate as $rateKey => $rateValue) {
$domains[$oneDomain][$rateKey] = $rateValue;
}
}
$this->config->saveConfig([self::SENDING_METHOD_ID.'_domains' => json_encode($domains)]);
}
private function getVerifiedDomains(): array
{
$verifiedDomains = [];
$domainStatuses = json_decode($this->config->get(self::SENDING_METHOD_ID.'_domains', '[]'), true);
if (!empty($domainStatuses)) {
foreach ($domainStatuses as $domain => $info) {
if ($info['status'] === 'SUCCESS') {
$verifiedDomains[] = $domain;
}
}
}
return $verifiedDomains;
}
private function displayMessage($message): string
{
$correspondances = [
'AlreadyExistsException' => 'ACYM_DOMAIN_X_ALREADY_EXIST',
'TooManyRequestsException' => 'ACYM_DOMAIN_TO_MANY_REQUEST',
'NotFoundException' => 'ACYM_DOMAIN_NOT_FOUND',
'NotExistException' => 'ACYM_CANT_DELETE_DOMAIN',
];
if (empty($message) || empty($correspondances[$message])) {
return 'ACYM_DOMAIN_DEFAULT_ERROR';
}
return $correspondances[$message];
}
private function getRemainingCredits($forceReload = false)
{
$time = time();
$lastCheck = $this->config->get(self::SENDING_METHOD_ID.'_last_credits_check', 0);
$lastDetails = $this->config->get(self::SENDING_METHOD_ID.'_credits_details', []);
if (!$forceReload && !empty($lastDetails) && $lastCheck > $time - self::CREDITS_RELOAD_DELAY) {
return json_decode($lastDetails, true);
}
$response = $this->callApiSendingMethod('get_credits');
$this->config->saveConfig([
self::SENDING_METHOD_ID.'_credits_details' => json_encode($response),
self::SENDING_METHOD_ID.'_last_credits_check' => $time,
]);
if (!empty($response['domains'])) {
$this->updateDomainRates($response['domains']);
}
return $response;
}
private function checkSuccessCode($responseCode): bool
{
// we can add more success response codes if needed but for now, that's all the success response codes the API can send
return in_array($responseCode, [200, 201]);
}
private function translate($response)
{
if (!isset($response['code']) && empty($response['message'])) {
return '';
}
$correspondence = '';
$messageData = [];
if (isset($response['code']) && !empty(self::TRANSLATIONS[$response['code']])) {
$correspondence = self::TRANSLATIONS[$response['code']];
}
if (!empty($response['messageData'])) {
$messageData = $response['messageData'];
}
if (empty($correspondence)) {
if (empty($messageData)) {
return $response['message'];
}
return acym_translationVsprintf($response['message'], $messageData, false);
}
if (empty($messageData)) {
$finalMessage = acym_translation($correspondence);
} else {
$finalMessage = acym_translationVsprintf($correspondence, $messageData);
}
if (in_array($response['code'], $this->errorCodes)) {
$this->errors[] = $finalMessage;
$this->errorCallback();
}
return $finalMessage;
}
/**
* Emails not needing an unsubscribe link:
* - Sending a test from config, from campaign tab or from editor
* - Forwarding a bounce message
* - Sending a spam-test
* - Overriding website emails
* - Sending notification messages
* - Unsubscribe emails
* - Sending the walkthrough email
* - Forwarding an email to a specific address from mailbox actions
*/
private function isUnsubscribeLinkMissing($mailerHelper): bool
{
if ($mailerHelper->isTest || $mailerHelper->isForward || $mailerHelper->isSpamTest || empty($mailerHelper->mail)) {
return false;
}
if (in_array($mailerHelper->mail->type, MailClass::TYPES_TRANSACTIONAL) || $mailerHelper->mail->type === MailClass::TYPE_AUTOMATION) {
return false;
}
return !$this->isContainingUnsubscribeLink($mailerHelper->mail->body);
}
private function isContainingUnsubscribeLink(string $text): bool
{
return strpos($text, '{unsubscribe}') !== false
|| strpos($text, '{unsubscribeall}') !== false
|| strpos($text, 'task=unsubscribe') !== false
|| strpos($text, 'frontusers/unsubscribe') !== false
|| strpos($text, '{direct_unsubscribe') !== false;
}
private function checkDomainsDNS($domains): array
{
$remainingDomains = [];
foreach ($domains as $oneDomain) {
// No DNS entries yet, we have to check the domain to get DNS entries
if (empty($oneDomain['CnameRecords'])) {
$remainingDomains[] = $oneDomain;
continue;
}
// Check if all DNS entries are valid before calling the API
foreach ($oneDomain['CnameRecords'] as $oneRecord) {
if (!$this->checkCnameEntry($oneRecord['name'], $oneRecord['value'])) {
$domainPosition = strpos($oneRecord['name'], '_domainkey') + 11;
$domainName = substr($oneRecord['name'], $domainPosition);
acym_sendAjaxResponse(acym_translation('ACYM_A_CNAME_MISSING'), ['domain' => $domainName], false);
}
}
$remainingDomains[] = $oneDomain;
}
return $remainingDomains;
}
private function checkCnameEntry(string $entryName, string $entryValue): bool
{
$domainPosition = strpos($entryName, '_domainkey') + 11;
$domainName = substr($entryName, $domainPosition);
$dnsEntries = @dns_get_record($entryName, DNS_CNAME);
if (empty($dnsEntries)) {
$entryName = substr($entryName, 0, $domainPosition);
$doubleDomainEntry = $entryName.'.'.$domainName;
$dnsEntries = @dns_get_record($doubleDomainEntry, DNS_CNAME);
if (!empty($dnsEntries)) {
$this->cnameErrors[] = acym_translationSprintf('ACYM_ACYMAILER_MALFORMED_CNAME', $doubleDomainEntry);
$this->cnameErrors[] = acym_translationSprintf('ACYM_ACYMAILER_CNAME_TRY_WITHOUT_DOMAIN', $entryName, $entryValue);
} else {
$this->cnameErrors[] = acym_translation('ACYM_WALK_ACYMAILER_STATUS_WAIT');
}
return false;
}
if (!is_array($dnsEntries)) {
$dnsEntries = [$dnsEntries];
}
foreach ($dnsEntries as $oneEntry) {
if ($oneEntry['type'] !== 'CNAME' || $oneEntry['host'] !== $entryName) {
continue;
}
if (rtrim($oneEntry['target'], '.') === rtrim($entryValue, '.')) {
return true;
}
$this->cnameErrors[] = acym_translationSprintf('ACYM_DOMAIN_CHECK_INCORRECT_VALUE', $entryName, $domainName);
return false;
}
$this->cnameErrors[] = acym_translation('ACYM_DNS_NOT_CNAME');
return false;
}
private function displayCnameStatus(array $cnameRecord): void
{
if ($cnameRecord['isValid']) {
echo '';
} else {
acym_tooltip(
[
'hoveredText' => '',
'textShownInTooltip' => implode('
', $cnameRecord['tooltipMessages']),
]
);
}
}
}