| @@ -9,8 +9,11 @@ | ||
| 9 | 9 | class QueueClass extends AcymClass |
| 10 | 10 | { |
| 11 | 11 | public array $emailtypes = []; |
| 12 | 12 | |
| 13 | + /** | |
| 14 | + * Get campaigns depending on filters (search, status, pagination) | |
| 15 | + */ | |
| 13 | 16 | public function getMatchingCampaigns(array $settings): array |
| 14 | 17 | { |
| 15 | 18 | $queuedMails = acym_loadResultArray('SELECT DISTINCT mail_id FROM #__acym_queue'); |
| 16 | 19 | if (empty($queuedMails)) { |
| @@ -57,8 +60,9 @@ | ||
| 57 | 60 | LEFT JOIN #__acym_campaign AS campaign |
| 58 | 61 | ON mail.id = campaign.mail_id |
| 59 | 62 | OR mail.parent_id = campaign.mail_id'; |
| 60 | 63 | |
| 64 | + // This query returns an array like "number of mails" => score. cf the equivalent in the list class to understand how it works | |
| 61 | 65 | $queryStatus = 'SELECT COUNT(DISTINCT mail.id) AS number, campaign.active |
| 62 | 66 | FROM #__acym_mail AS mail |
| 63 | 67 | LEFT JOIN #__acym_campaign AS campaign ON mail.id = campaign.mail_id'; |
| 64 | 68 | |
| @@ -89,9 +93,9 @@ | ||
| 89 | 93 | 'followup' => 'mail.type = '.acym_escapeDB(MailClass::TYPE_FOLLOWUP), |
| 90 | 94 | ]; |
| 91 | 95 | |
| 92 | 96 | if (empty($allowedStatus[$settings['status']])) { |
| 93 | - die('Unauthorized filter: '.acym_escape($settings['status'])); | |
| 97 | + die('Unauthorized filter: '.esc_html($settings['status'])); | |
| 94 | 98 | } |
| 95 | 99 | |
| 96 | 100 | $filters[] = $allowedStatus[$settings['status']]; |
| 97 | 101 | } |
| @@ -107,10 +111,10 @@ | ||
| 107 | 111 | ]; |
| 108 | 112 | |
| 109 | 113 | $isMultilingual = acym_isMultilingual(); |
| 110 | 114 | $campaignRecipientsMultilingual = []; |
| 111 | - $automationHelper = new AutomationHelper(); | |
| 112 | 115 | |
| 116 | + // Get the recipients | |
| 113 | 117 | $specialTypes = []; |
| 114 | 118 | acym_trigger('getCampaignTypes', [&$specialTypes]); |
| 115 | 119 | |
| 116 | 120 | foreach ($results['elements'] as $i => $oneMail) { |
| @@ -147,8 +151,10 @@ | ||
| 147 | 151 | $results['elements'][$i]->recipients = intval($mailStatClass->getTotalSubscribersByMailIdWithChild($mailId)); |
| 148 | 152 | } |
| 149 | 153 | } elseif ($isMultilingual) { |
| 150 | 154 | if (empty($campaignRecipientsMultilingual[$oneMail->campaign])) { |
| 155 | + $automationHelper = new AutomationHelper(); | |
| 156 | + | |
| 151 | 157 | $listIds = array_keys($results['elements'][$i]->lists); |
| 152 | 158 | acym_arrayToInteger($listIds); |
| 153 | 159 | |
| 154 | 160 | $automationHelper->join['user_list'] = ' #__acym_user_has_list AS user_list ON user_list.user_id = user.id AND user_list.list_id IN ('.implode( |
| @@ -233,8 +239,11 @@ | ||
| 233 | 239 | |
| 234 | 240 | return $results; |
| 235 | 241 | } |
| 236 | 242 | |
| 243 | + /** | |
| 244 | + * Get campaigns depending on filters (search, status, pagination) | |
| 245 | + */ | |
| 237 | 246 | public function getMatchingScheduledCampaigns(array $settings): array |
| 238 | 247 | { |
| 239 | 248 | $mailClass = new MailClass(); |
| 240 | 249 | $query = 'FROM #__acym_mail AS mail |
| @@ -277,8 +286,11 @@ | ||
| 277 | 286 | |
| 278 | 287 | return $results; |
| 279 | 288 | } |
| 280 | 289 | |
| 290 | + /** | |
| 291 | + * Get mails depending on filters (search, status, pagination) | |
| 292 | + */ | |
| 281 | 293 | public function getMatchingResults(array $settings): array |
| 282 | 294 | { |
| 283 | 295 | $query = 'FROM #__acym_queue AS queue |
| 284 | 296 | JOIN #__acym_mail AS mail ON mail.id = queue.mail_id |
| @@ -304,13 +316,15 @@ | ||
| 304 | 316 | if (!empty($filters)) { |
| 305 | 317 | $query .= ' WHERE ('.implode(') AND (', $filters).')'; |
| 306 | 318 | } |
| 307 | 319 | |
| 308 | - if (!empty($settings['tag'])) { | |
| 320 | + if (empty($settings['tag'])) { | |
| 321 | + $queryCount = 'SELECT COUNT(queue.mail_id) AS total '.$query; | |
| 322 | + } else { | |
| 323 | + $queryCount = 'SELECT COUNT(DISTINCT queue.mail_id, queue.user_id) AS total '.$query; | |
| 309 | 324 | $query .= ' GROUP BY queue.mail_id, queue.user_id'; |
| 310 | 325 | } |
| 311 | 326 | |
| 312 | - $queryCount = 'SELECT COUNT(queue.mail_id) AS total '.$query; | |
| 313 | 327 | $query = 'SELECT mail.id, queue.sending_date, mail.name, mail.subject, user.email, user.name AS user_name, queue.user_id, queue.try '.$query.' ORDER BY queue.sending_date ASC'; |
| 314 | 328 | |
| 315 | 329 | $mailClass = new MailClass(); |
| 316 | 330 | |
| @@ -348,8 +362,9 @@ | ||
| 348 | 362 | |
| 349 | 363 | $nbQueue = []; |
| 350 | 364 | |
| 351 | 365 | foreach ($mailReady as $mailId => $mail) { |
| 366 | + // A/B test campaigns are queued later by CampaignClass::send(), so skip queueing here | |
| 352 | 367 | $sendingParams = $mail->sending_params ?? []; |
| 353 | 368 | if (is_string($sendingParams)) { |
| 354 | 369 | $sendingParams = json_decode($sendingParams, true) ?? []; |
| 355 | 370 | } |
| @@ -465,8 +480,9 @@ | ||
| 465 | 480 | if (!empty($mailId)) { |
| 466 | 481 | $query .= ' AND queue.`mail_id` = '.intval($mailId); |
| 467 | 482 | } |
| 468 | 483 | |
| 484 | + // We don't display this option in the configuration anymore but we use its value if it's set in the database | |
| 469 | 485 | $sendOrder = $this->config->get('sendorder'); |
| 470 | 486 | if (empty($sendOrder)) { |
| 471 | 487 | $order = 'queue.`user_id` ASC'; |
| 472 | 488 | } elseif ($sendOrder === 'rand') { |
| @@ -473,12 +489,13 @@ | ||
| 473 | 489 | $order = 'RAND()'; |
| 474 | 490 | } else { |
| 475 | 491 | $sendOrder = str_replace('subid', 'user_id', $sendOrder); |
| 476 | 492 | $ordering = explode(',', $sendOrder); |
| 477 | - $order = 'queue.`'.acym_secureDBColumn(trim($ordering[0])).'` '.acym_secureDBColumn(trim($ordering[1])); | |
| 493 | + $order = 'queue.`'.acym_secureDBColumn(trim($ordering[0])).'` '.acym_secureDBColumn(trim($ordering[1] ?? 'ASC')); | |
| 478 | 494 | } |
| 479 | 495 | |
| 480 | 496 | $query .= ' ORDER BY queue.`priority` ASC, queue.`sending_date` ASC, '.$order; |
| 497 | + // You can add a "startqueue" parameter to the url so Acy will not load the first e-mails but will start directly with the 300 or 500 or... | |
| 481 | 498 | $query .= ' LIMIT '.intval($startFrom).','.intval($limit); |
| 482 | 499 | |
| 483 | 500 | try { |
| 484 | 501 | $results = acym_loadObjectList($query); |
| @@ -486,8 +503,9 @@ | ||
| 486 | 503 | $results = null; |
| 487 | 504 | } |
| 488 | 505 | |
| 489 | 506 | if ($results === null) { |
| 507 | + // We got an issue here... maybe the table is crashed so we will repair it. | |
| 490 | 508 | acym_query('REPAIR TABLE #__acym_queue, #__acym_user, #__acym_mail, #__acym_campaign'); |
| 491 | 509 | } |
| 492 | 510 | |
| 493 | 511 | if (empty($results)) { |
| @@ -493,8 +511,12 @@ | ||
| 493 | 511 | if (empty($results)) { |
| 494 | 512 | return []; |
| 495 | 513 | } |
| 496 | 514 | |
| 515 | + // This comment doesn't make any sense | |
| 516 | + //We update the first entry from the queue and change its sending_date with +1 so it does not get sent immediately after in case of we had an issue (a time out execution)... | |
| 517 | + //That way e-mails which can't be sent will be sent at the end and we will be able to clean the queue and don't care about what's left in the queue any more | |
| 518 | + //Also it will avoid the same user to receive messages again and again and again in case of there is a problem | |
| 497 | 519 | $firstElementQueued = reset($results); |
| 498 | 520 | acym_query( |
| 499 | 521 | 'UPDATE #__acym_queue |
| 500 | 522 | SET sending_date = DATE_ADD(sending_date, INTERVAL 1 SECOND) |
| @@ -556,11 +578,13 @@ | ||
| 556 | 578 | 'userlist.status = 1', |
| 557 | 579 | 'maillist.mail_id = '.intval(empty($mail->parent_id) ? $mail->id : $mail->parent_id), |
| 558 | 580 | ]; |
| 559 | 581 | |
| 582 | + // Send this version only to the users with the correct language | |
| 560 | 583 | if (!$onlyNew && acym_isMultilingual()) { |
| 561 | 584 | $where = 'user.language = '.acym_escapeDB($mail->language); |
| 562 | 585 | if ($mail->id == $mail->parent_id) { |
| 586 | + //TODO check if a user with no language will receive every versions or only the main one | |
| 563 | 587 | $where .= ' OR user.language = "" OR user.language NOT IN (SELECT language FROM #__acym_mail WHERE parent_id = '.intval($mail->id).')'; |
| 564 | 588 | } |
| 565 | 589 | $automationHelper->where[] = $where; |
| 566 | 590 | } |
| @@ -574,8 +598,9 @@ | ||
| 574 | 598 | $mail->id |
| 575 | 599 | ).' OR id = '.intval($mail->id).')'; |
| 576 | 600 | $automationHelper->where[] = '`us`.`user_id` IS NULL'; |
| 577 | 601 | |
| 602 | + // Do not count the disabled user for the resend counter on summary | |
| 578 | 603 | $automationHelper->where[] = '`user`.`active` = 1'; |
| 579 | 604 | } |
| 580 | 605 | |
| 581 | 606 | $automationHelper->removeFlag(SegmentsController::FLAG_USERS); |
| @@ -580,12 +605,14 @@ | ||
| 580 | 605 | |
| 581 | 606 | $automationHelper->removeFlag(SegmentsController::FLAG_USERS); |
| 582 | 607 | $automationHelper->removeFlag(SegmentsController::FLAG_COUNT); |
| 583 | 608 | |
| 609 | + // Handle potential segment | |
| 584 | 610 | if (empty($mail->filters)) { |
| 585 | 611 | return $automationHelper; |
| 586 | 612 | } |
| 587 | 613 | |
| 614 | + // Mark users matching the segment | |
| 588 | 615 | foreach ($mail->filters as $orValues) { |
| 589 | 616 | if (empty($orValues)) continue; |
| 590 | 617 | |
| 591 | 618 | $automationHelperSegment = new AutomationHelper(); |
| @@ -609,8 +636,9 @@ | ||
| 609 | 636 | |
| 610 | 637 | public function queue(object $mail): int |
| 611 | 638 | { |
| 612 | 639 | $automationHelper = $this->getMailReceivers($mail); |
| 640 | + // Only queue enabled users | |
| 613 | 641 | $automationHelper->where[] = '`user`.`active` = 1'; |
| 614 | 642 | |
| 615 | 643 | $priority = $this->config->get('priority_newsletter', 3); |
| 616 | 644 | $select = [intval($mail->id), 'userlist.user_id', acym_escapeDB($mail->sending_date), intval($priority), '0']; |