PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.1
11.1.1 11.1.0 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 All 62 releases
← All changes | back/Helpers/QueueHelper.php +0 -576 10.10.2 → 11.1.1 View file →
@@ -1,576 +1,0 @@
1 -<?php
2 -
3 -namespace AcyMailing\Helpers;
4 -
5 -use AcyMailing\Classes\MailStatClass;
6 -use AcyMailing\Classes\QueueClass;
7 -use AcyMailing\Classes\UserClass;
8 -use AcyMailing\Classes\UserStatClass;
9 -use AcyMailing\Core\AcymObject;
10 -
11 -class QueueHelper extends AcymObject
12 -{
13 - const MESSAGE_TYPE_SUCCESS = 1;
14 - const MESSAGE_TYPE_WARNING = 2;
15 - const MESSAGE_TYPE_ERROR = 3;
16 -
17 - public bool $report = true;
18 - public bool $finish = false;
19 - public bool $fromManual = false;
20 - public int $id = 0;
21 - public int $send_limit = 0;
22 - public int $nbprocess = 0;
23 - public int $startSendingFrom = 0;
24 - public int $start = 0;
25 - public int $stoptime = 0;
26 - public int $successSend = 0;
27 - public int $errorSend = 0;
28 - public int $pause = 0;
29 - public int $total;
30 - public array $messages = [];
31 - public array $emailTypes = [];
32 -
33 - private int $consecutiveError = 0;
34 - private bool $mod_security2 = false;
35 - private int $obend = 0;
36 -
37 - private UserClass $userClass;
38 - private QueueClass $queueClass;
39 -
40 - public function __construct()
41 - {
42 - parent::__construct();
43 -
44 - $this->queueClass = new QueueClass();
45 - $this->userClass = new UserClass();
46 -
47 - $this->send_limit = (int)$this->config->get('queue_nbmail', 40);
48 -
49 - @ini_set('default_socket_timeout', 10);
50 -
51 - @ignore_user_abort(true);
52 -
53 - $timeLimit = acym_increasePerf();
54 - if (empty($timeLimit)) {
55 - $timeLimit = 600;
56 - }
57 -
58 - $this->stoptime = time() + $timeLimit - 4;
59 - }
60 -
61 - public function process(): bool
62 - {
63 - $creditsLeft = 10000;
64 - acym_trigger('onAcymCreditsLeft', [&$creditsLeft]);
65 -
66 - if (empty($creditsLeft)) {
67 - $this->finish = true;
68 - if ($this->report) {
69 - acym_display(acym_translation('ACYM_NOT_ENOUGH_CREDITS'), 'info');
70 - }
71 -
72 - return false;
73 - }
74 -
75 - if ($this->send_limit > $creditsLeft) {
76 - $this->send_limit = $creditsLeft;
77 - }
78 -
79 - $queueClass = new QueueClass();
80 - $queueClass->emailtypes = $this->emailTypes;
81 - $queueElements = $queueClass->getReady($this->startSendingFrom, $this->send_limit, $this->id);
82 -
83 - if (empty($queueElements)) {
84 - $this->finish = true;
85 - if ($this->report) {
86 - acym_display(acym_translation('ACYM_NO_PROCESS'), 'info');
87 - }
88 -
89 - return true;
90 - }
91 -
92 - if ($this->report) {
93 - if (function_exists('apache_get_modules')) {
94 - $modules = apache_get_modules();
95 - $this->mod_security2 = in_array('mod_security2', $modules);
96 - }
97 -
98 - @ini_set('output_buffering', 'off');
99 - @ini_set('zlib.output_compression', 0);
100 -
101 - if (!headers_sent()) {
102 - while (ob_get_level() > 0 && $this->obend++ < 3) {
103 - @ob_end_flush();
104 - }
105 - }
106 -
107 - $disp = '<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />';
108 - $disp .= '<title>'.acym_translation('ACYM_SEND_PROCESS').'</title>';
109 - $disp .= '<style>body{font-size:12px;font-family: Arial,Helvetica,sans-serif;}</style></head><body>';
110 - $disp .= '<div style="margin-bottom: 18px;padding: 8px !important; background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px;"><p style="margin:0;">'.acym_translation(
111 - 'ACYM_DONT_CLOSE'
112 - ).'</p></div>';
113 - $disp .= "<div style='display: inline;background-color : white;border : 1px solid grey; padding : 3px;font-size:14px'>";
114 - $disp .= "<span id='divpauseinfo' style='padding:10px;margin:5px;font-size:16px;font-weight:bold;display:none;background-color:black;color:white;'> </span>";
115 - $disp .= acym_translation('ACYM_SEND_PROCESS').': <span id="counter" >'.$this->start.'</span> / '.$this->total;
116 - $disp .= '</div>';
117 - $disp .= "<div id='divinfo' style='display:none; position:fixed; bottom:3px;left:3px;background-color : white; border : 1px solid grey; padding : 3px;'> </div>";
118 - $disp .= '<br /><br />';
119 - $url = acym_completeLink('queue&task=continuesend&id='.$this->id.'&totalsend='.$this->total, true, true).'&alreadysent=';
120 - $disp .= '<script type="text/javascript" language="javascript">';
121 - $disp .= 'var mycounter = document.getElementById("counter");';
122 - $disp .= 'var divinfo = document.getElementById("divinfo");
123 - var divpauseinfo = document.getElementById("divpauseinfo");
124 - function setInfo(message){ divinfo.style.display = \'block\';divinfo.innerHTML=message; }
125 - function setPauseInfo(nbpause){ divpauseinfo.style.display = \'\';divpauseinfo.innerHTML=nbpause;}
126 - function setCounter(val){ mycounter.innerHTML=val;}
127 - var scriptpause = '.$this->pause.';
128 - function handlePause(){
129 - setPauseInfo(scriptpause);
130 - if(scriptpause > 0){
131 - scriptpause = scriptpause - 1;
132 - setTimeout(\'handlePause()\',1000);
133 - }else{
134 - document.location.href=\''.$url.'\'+mycounter.innerHTML;
135 - }
136 - }
137 - </script>';
138 - echo $disp;
139 - if (function_exists('ob_flush')) {
140 - @ob_flush();
141 - }
142 - if (!$this->mod_security2) {
143 - @flush();
144 - }
145 - }
146 -
147 - $mailerHelper = new MailerHelper();
148 - $mailerHelper->report = false;
149 - if ($this->config->get('smtp_keepalive', 1) || $this->config->get('mailer_method') === 'elasticemail') {
150 - $mailerHelper->SMTPKeepAlive = true;
151 - }
152 -
153 - $queueDelete = [];
154 - $queueUpdate = [];
155 - $statsAdd = [];
156 -
157 - $maxTry = (int)$this->config->get('queue_try', 0);
158 -
159 - $currentMail = $this->start;
160 - $this->nbprocess = 0;
161 -
162 - if (count($queueElements) < $this->send_limit) {
163 - $this->finish = true;
164 - }
165 -
166 - $externalSending = false;
167 - acym_trigger('onAcymProcessQueueExternalSendingCampaign', [&$externalSending]);
168 -
169 - if ($externalSending) {
170 - $listExternalSending = [];
171 - if (!empty($this->id)) {
172 - acym_trigger('onAcymInitExternalSendingMethodBeforeSend', [&$listExternalSending, $this->id]);
173 - } else {
174 - $mailIds = [];
175 - foreach ($queueElements as $oneElement) {
176 - if (!in_array($oneElement->mail_id, $mailIds)) {
177 - $mailIds[] = $oneElement->mail_id;
178 - acym_trigger('onAcymInitExternalSendingMethodBeforeSend', [&$listExternalSending, $oneElement->mail_id]);
179 - }
180 - }
181 - }
182 - }
183 -
184 - $sentEmails = [];
185 - $emailFrequency = $this->fromManual ? 0 : intval($this->config->get('email_frequency', 0));
186 - if ($emailFrequency < 0) {
187 - $emailFrequency = 0;
188 - }
189 -
190 - $userLists = [];
191 - $userClass = new UserClass();
192 - $isSendingMethodByListActive = false;
193 - acym_trigger('sendingMethodByListActive', [&$isSendingMethodByListActive]);
194 - $mailerHelper->isSendingMethodByListActive = $isSendingMethodByListActive;
195 -
196 - $statisticsByBatch = $this->config->get('queue_statistics_by_batch', 1) == 1;
197 -
198 - foreach ($queueElements as $oneQueue) {
199 - if ($isSendingMethodByListActive && empty($userLists[$oneQueue->user_id])) {
200 - $userSubscriptions = $userClass->getUserStandardListIdById($oneQueue->user_id);
201 - $userLists[$oneQueue->user_id] = empty($userSubscriptions) ? [] : $userSubscriptions;
202 - }
203 - if (!empty($emailFrequency)) {
204 - sleep($emailFrequency);
205 - }
206 -
207 - $currentMail++;
208 - $this->nbprocess++;
209 - if ($this->report) {
210 - echo '<script type="text/javascript">setCounter('.$currentMail.')</script>';
211 - if (function_exists('ob_flush')) {
212 - @ob_flush();
213 - }
214 - if (!$this->mod_security2) {
215 - @flush();
216 - }
217 - }
218 -
219 - $isAbTesting = false;
220 -
221 - if (!empty($oneQueue->sending_params)) {
222 - $sendingParams = json_decode($oneQueue->sending_params, true);
223 -
224 - $isAbTesting = isset($sendingParams['abtest']);
225 - }
226 -
227 - $this->triggerSendHook((int)$oneQueue->mail_id);
228 - try {
229 - $mailerHelper->isAbTest = $isAbTesting;
230 - $mailerHelper->listsIds = empty($userLists[$oneQueue->user_id]) ? [] : $userLists[$oneQueue->user_id];
231 - $mailerHelper->setSendingMethodSetting();
232 - $result = $mailerHelper->sendOne($oneQueue->mail_id, $oneQueue->user_id);
233 - } catch (\Exception $e) {
234 - $result = false;
235 - $this->displayMessage([$e->getMessage()], self::MESSAGE_TYPE_ERROR, $e->getCode());
236 - }
237 -
238 - if (empty($sentEmails[$oneQueue->mail_id])) {
239 - $sentEmails[$oneQueue->mail_id] = $mailerHelper->Body;
240 - }
241 -
242 - $queueDeleteOk = true;
243 - $otherMessage = '';
244 -
245 - if ($result === true) {
246 - $this->successSend++;
247 - $this->consecutiveError = 0;
248 - $queueDelete[$oneQueue->mail_id][] = $oneQueue->user_id;
249 - $statsAdd[$oneQueue->mail_id][1][] = $oneQueue->user_id;
250 -
251 - $queueDeleteOk = $this->deleteQueue($queueDelete);
252 - $mailerHelper->triggerFollowUpAgain($oneQueue->mail_id, $oneQueue->user_id);
253 - $queueDelete = [];
254 -
255 - if (!$statisticsByBatch || $this->nbprocess % 10 == 0) {
256 - $this->statsAdd($statsAdd);
257 - $this->queueUpdate($queueUpdate);
258 - $statsAdd = [];
259 - $queueUpdate = [];
260 - }
261 - } elseif ($mailerHelper->dtextsFailed) {
262 - $this->consecutiveError = 0;
263 - $queueDelete[$oneQueue->mail_id][] = $oneQueue->user_id;
264 - $queueDeleteOk = $this->deleteQueue($queueDelete);
265 - $queueDelete = [];
266 - } else {
267 - acym_trigger('onAcymFailedSendingEmail', [$oneQueue->mail_id, $oneQueue->user_id, $mailerHelper->errorNumber]);
268 - $this->errorSend++;
269 -
270 - $shouldTrySendingLater = false;
271 - if (in_array($mailerHelper->errorNumber, MailerHelper::NEW_TRY_ERRORS)) {
272 - if (empty($maxTry) || $oneQueue->try < $maxTry - 1) {
273 - $shouldTrySendingLater = true;
274 - if ($mailerHelper->failedCounting) {
275 - $otherMessage = acym_translationSprintf('ACYM_QUEUE_NEXT_TRY', 60);
276 - }
277 - }
278 -
279 - if ($mailerHelper->errorNumber === 1) {
280 - $this->consecutiveError++;
281 - }
282 -
283 - if ($this->consecutiveError === 2) {
284 - sleep(1);
285 - }
286 - }
287 -
288 - if (!$shouldTrySendingLater) {
289 - $queueDelete[$oneQueue->mail_id][] = $oneQueue->user_id;
290 - $statsAdd[$oneQueue->mail_id][0][] = $oneQueue->user_id;
291 - if ($mailerHelper->errorNumber === 1 && $this->config->get('bounce_action_maxtry')) {
292 - $queueDeleteOk = $this->deleteQueue($queueDelete);
293 - $queueDelete = [];
294 - $otherMessage .= $this->failedActions((int)$oneQueue->user_id);
295 - }
296 - } elseif ($mailerHelper->failedCounting) {
297 - $queueUpdate[$oneQueue->mail_id][] = $oneQueue->user_id;
298 - }
299 -
300 - $mailerHelper->failedCounting = true;
301 - }
302 -
303 - $messageOnScreen = '[ID '.$oneQueue->mail_id.'] '.$mailerHelper->reportMessage;
304 - if (!empty($otherMessage)) {
305 - $messageOnScreen .= ' => '.$otherMessage;
306 - }
307 - $this->displayMessage([$messageOnScreen], $result ? self::MESSAGE_TYPE_SUCCESS : self::MESSAGE_TYPE_ERROR, $currentMail);
308 -
309 - if (!$queueDeleteOk) {
310 - $this->finish = true;
311 - break;
312 - }
313 -
314 - if (!empty($this->stoptime) && $this->stoptime < time()) {
315 - $this->displayMessage([acym_translation('ACYM_SEND_REFRESH_TIMEOUT')], self::MESSAGE_TYPE_WARNING);
316 - if ($this->nbprocess < count($queueElements)) {
317 - $this->finish = false;
318 - }
319 - break;
320 - }
321 -
322 - if ($this->consecutiveError > 3 && $this->successSend > 3) {
323 - $this->displayMessage([acym_translation('ACYM_SEND_REFRESH_CONNECTION')], self::MESSAGE_TYPE_WARNING);
324 - break;
325 - }
326 -
327 - if ($this->consecutiveError > 5 || ($this->report && connection_aborted())) {
328 - $this->finish = true;
329 - break;
330 - }
331 - }
332 -
333 - if ($externalSending) {
334 - foreach ($sentEmails as $mailId => $content) {
335 - if (!$queueClass->isSendingFinished($mailId)) continue;
336 -
337 - acym_trigger('onAcymSendCampaignOnExternalSendingMethod', [$mailId, $content]);
338 - }
339 - }
340 -
341 - $this->deleteQueue($queueDelete);
342 - $this->statsAdd($statsAdd);
343 - $this->queueUpdate($queueUpdate);
344 -
345 - if ($mailerHelper->SMTPKeepAlive) {
346 - $mailerHelper->smtpClose();
347 - }
348 -
349 - if (!empty($this->total) && $currentMail >= $this->total) {
350 - $this->finish = true;
351 - }
352 -
353 - if ($this->consecutiveError > 5) {
354 - $this->handleError();
355 -
356 - return false;
357 - }
358 -
359 - if ($this->report && !$this->finish) {
360 - echo '<script type="text/javascript" language="javascript">handlePause();</script>';
361 - }
362 -
363 - if ($this->report) {
364 - echo '</body></html>';
365 - while ($this->obend-- > 0) {
366 - ob_start();
367 - }
368 - exit;
369 - }
370 -
371 - return true;
372 - }
373 -
374 - public function statsAdd(array $statsAdd): void
375 - {
376 - if (empty($statsAdd)) {
377 - return;
378 - }
379 -
380 - $userStatClass = new UserStatClass();
381 - $mailStatClass = new MailStatClass();
382 -
383 - $currentDate = acym_date('now', 'Y-m-d H:i:s', false);
384 - $subscriberIds = [];
385 -
386 - foreach ($statsAdd as $mailId => $infos) {
387 - $mailId = intval($mailId);
388 -
389 - foreach ($infos as $status => $subscribers) {
390 - foreach ($subscribers as $oneSubscriber) {
391 - $oneSubscriber = intval($oneSubscriber);
392 -
393 - $userStat = new \stdClass();
394 - $userStat->user_id = $oneSubscriber;
395 - $userStat->mail_id = $mailId;
396 - $userStat->send_date = $currentDate;
397 - $userStat->fail = $status ? 0 : 1;
398 - $userStat->sent = $status ? 1 : 0;
399 - $userStat->statusSending = $status;
400 -
401 - $userStatClass->save($userStat, true);
402 -
403 - if ($status) {
404 - $subscriberIds[] = $oneSubscriber;
405 - }
406 - }
407 - }
408 -
409 - $nbSent = empty($infos[1]) ? 0 : count($infos[1]);
410 - $nbFail = empty($infos[0]) ? 0 : count($infos[0]);
411 -
412 - $mailStat = new \stdClass();
413 - $mailStat->mail_id = $mailId;
414 - $mailStat->sent = $nbSent;
415 - $mailStat->fail = $nbFail;
416 -
417 - $mailStatClass->save($mailStat);
418 - }
419 -
420 - if (!empty($subscriberIds)) {
421 - acym_query('UPDATE `#__acym_user` SET `last_sent_date` = '.acym_escapeDB($currentDate).' WHERE `id` IN ('.implode(',', $subscriberIds).')');
422 - }
423 - }
424 -
425 - private function triggerSendHook(int $mailId): void
426 - {
427 - static $triggered = [];
428 - if (!empty($triggered[$mailId])) {
429 - return;
430 - }
431 -
432 - $triggered[$mailId] = true;
433 - acym_triggerCmsHook('onAcymSendMail', [$mailId]);
434 - }
435 -
436 - private function deleteQueue(array $queueDelete): bool
437 - {
438 - if (empty($queueDelete)) {
439 - return true;
440 - }
441 -
442 - $status = true;
443 -
444 - foreach ($queueDelete as $mailId => $userIds) {
445 - $nbUsers = count($userIds);
446 - $nbDeleted = $this->queueClass->deleteQueuedByUserIds($userIds, $mailId);
447 -
448 - if (!empty($this->queueClass->errors)) {
449 - $status = false;
450 - $this->displayMessage($this->queueClass->errors, self::MESSAGE_TYPE_ERROR);
451 - } else {
452 - if ($nbDeleted !== $nbUsers) {
453 - $status = false;
454 - $this->displayMessage(
455 - [$nbDeleted < $nbUsers ? acym_translation('ACYM_QUEUE_DOUBLE') : $nbDeleted.' emails deleted from the queue whereas we only have '.$nbUsers.' subscribers'],
456 - self::MESSAGE_TYPE_ERROR
457 - );
458 - }
459 - }
460 - }
461 -
462 - return $status;
463 - }
464 -
465 - private function queueUpdate(array $queueUpdate): void
466 - {
467 - if (empty($queueUpdate)) {
468 - return;
469 - }
470 -
471 - foreach ($queueUpdate as $mailid => $subscribers) {
472 - $this->queueClass->delayFailed($mailid, $subscribers);
473 - }
474 - }
475 -
476 - private function handleError(): void
477 - {
478 - $this->finish = true;
479 - $message = acym_translation('ACYM_SEND_STOPED');
480 - $message .= '<br />';
481 - $message .= acym_translation('ACYM_SEND_KEPT_ALL');
482 - $message .= '<br />';
483 - if ($this->report) {
484 - if (empty($this->successSend) && empty($this->start)) {
485 - $message .= acym_translation('ACYM_SEND_CHECKONE');
486 - $message .= '<br />';
487 - $message .= acym_translation('ACYM_SEND_ADVISE_LIMITATION');
488 - } else {
489 - $message .= acym_translation('ACYM_SEND_REFUSE');
490 - $message .= '<br />';
491 - if (!acym_level(ACYM_ESSENTIAL)) {
492 - $message .= acym_translation('ACYM_SEND_CONTINUE_COMMERCIAL');
493 - } else {
494 - $message .= acym_translation('ACYM_SEND_CONTINUE_AUTO');
495 - }
496 - }
497 - }
498 -
499 - $this->displayMessage([$message], self::MESSAGE_TYPE_WARNING);
500 - }
501 -
502 - private function displayMessage(array $messages, int $status, int $num = 0): void
503 - {
504 - foreach ($messages as $message) {
505 - $this->messages[] = strip_tags($message);
506 - }
507 -
508 - if (!$this->report) {
509 - return;
510 - }
511 -
512 - $color = $status === self::MESSAGE_TYPE_SUCCESS ? 'green' : ($status === self::MESSAGE_TYPE_WARNING ? 'orange' : 'red');
513 - foreach ($messages as $message) {
514 - if (!empty($num)) {
515 - echo '<br />'.$num.' : <span style="color:'.$color.';">'.$message.'</span>';
516 - } else {
517 - echo '<script type="text/javascript" language="javascript">setInfo(\''.addslashes($message).'\')</script>';
518 - }
519 - }
520 -
521 - if (function_exists('ob_flush')) {
522 - @ob_flush();
523 - }
524 - if (!$this->mod_security2) {
525 - @flush();
526 - }
527 - }
528 -
529 - private function failedActions(int $userId): string
530 - {
531 - $actionMaxTries = $this->config->get('bounce_action_maxtry');
532 - if (in_array($actionMaxTries, ['sub', 'remove', 'unsub'])) {
533 - $subscriptions = $this->userClass->getUserSubscriptionById($userId);
534 - }
535 -
536 - $listId = 0;
537 - $message = '';
538 - switch ($actionMaxTries) {
539 - case 'sub':
540 - $listId = $this->config->get('bounce_action_lists_maxtry');
541 - if (!empty($listId)) {
542 - $message .= ' user '.$userId.' subscribed to list n°'.$listId;
543 - $this->userClass->subscribe([$userId], [$listId]);
544 - }
545 - case 'remove':
546 - $unsubLists = array_diff(array_keys($subscriptions), [$listId]);
547 - if (!empty($unsubLists)) {
548 - $message .= ' user '.$userId.' removed from lists '.implode(',', $unsubLists);
549 - $this->userClass->removeSubscription([$userId], $unsubLists);
550 - } else {
551 - $message .= ' user '.$userId.' not subscribed';
552 - }
553 - break;
554 - case 'unsub':
555 - $unsubLists = array_diff(array_keys($subscriptions), [$listId]);
556 - if (!empty($unsubLists)) {
557 - $message .= ' user '.$userId.' unsubscribed from lists '.implode(',', $unsubLists);
558 - $this->userClass->unsubscribe([$userId], $unsubLists);
559 - } else {
560 - $message .= ' user '.$userId.' not unsubscribed';
561 - }
562 - break;
563 - case 'delete':
564 - $message .= ' user '.$userId.' deleted';
565 - $this->userClass->delete([$userId]);
566 - break;
567 - case 'block':
568 - $message .= ' user '.$userId.' blocked';
569 - $this->userClass->deactivate([$userId]);
570 - $this->queueClass->deleteQueuedByUserIds([$userId]);
571 - break;
572 - }
573 -
574 - return $message;
575 - }
576 -}