| @@ -7,28 +7,73 @@ | ||
| 7 | 7 | use AcyMailing\Core\AcymClass; |
| 8 | 8 | |
| 9 | 9 | class QueueClass extends AcymClass |
| 10 | 10 | { |
| 11 | - public $emailtypes; | |
| 11 | + public array $emailtypes = []; | |
| 12 | 12 | |
| 13 | - public function getMatchingCampaigns($settings) | |
| 13 | + /** | |
| 14 | + * Get campaigns depending on filters (search, status, pagination) | |
| 15 | + */ | |
| 16 | + public function getMatchingCampaigns(array $settings): array | |
| 14 | 17 | { |
| 18 | + $queuedMails = acym_loadResultArray('SELECT DISTINCT mail_id FROM #__acym_queue'); | |
| 19 | + if (empty($queuedMails)) { | |
| 20 | + return [ | |
| 21 | + 'elements' => [], | |
| 22 | + 'total' => (object)['total' => 0], | |
| 23 | + 'status' => [ | |
| 24 | + 'all' => 0, | |
| 25 | + 'sending' => 0, | |
| 26 | + 'paused' => 0, | |
| 27 | + 'automation' => 0, | |
| 28 | + 'followup' => 0, | |
| 29 | + ], | |
| 30 | + ]; | |
| 31 | + } | |
| 32 | + | |
| 15 | 33 | $campaignClass = new CampaignClass(); |
| 16 | 34 | $mailStatClass = new MailStatClass(); |
| 17 | 35 | $mailClass = new MailClass(); |
| 18 | - $query = 'FROM #__acym_mail AS mail | |
| 19 | - JOIN #__acym_queue AS queue ON mail.id = queue.mail_id | |
| 20 | - LEFT JOIN #__acym_campaign AS campaign ON mail.id = campaign.mail_id OR mail.parent_id = campaign.mail_id'; | |
| 21 | 36 | |
| 37 | + $queryCount = 'SELECT COUNT(DISTINCT mail.id) AS total | |
| 38 | + FROM #__acym_mail AS mail | |
| 39 | + LEFT JOIN #__acym_campaign AS campaign ON campaign.mail_id = mail.id OR campaign.mail_id = mail.parent_id'; | |
| 40 | + | |
| 41 | + $query = 'SELECT | |
| 42 | + mail.name, | |
| 43 | + mail.subject, | |
| 44 | + mail.type, | |
| 45 | + mail.id, | |
| 46 | + campaign.id AS campaign, | |
| 47 | + COALESCE(campaign.sending_date, queue.min_sending_date) AS sending_date, | |
| 48 | + campaign.sending_type, | |
| 49 | + campaign.active, | |
| 50 | + campaign.sending_params AS sending_params, | |
| 51 | + queue.nbqueued, | |
| 52 | + mail.language, | |
| 53 | + mail.parent_id | |
| 54 | + FROM #__acym_mail AS mail | |
| 55 | + JOIN ( | |
| 56 | + SELECT mail_id, COUNT(*) AS nbqueued, MIN(sending_date) AS min_sending_date | |
| 57 | + FROM #__acym_queue | |
| 58 | + GROUP BY mail_id | |
| 59 | + ) AS queue ON mail.id = queue.mail_id | |
| 60 | + LEFT JOIN #__acym_campaign AS campaign | |
| 61 | + ON mail.id = campaign.mail_id | |
| 62 | + OR mail.parent_id = campaign.mail_id'; | |
| 63 | + | |
| 64 | + // This query returns an array like "number of mails" => score. cf the equivalent in the list class to understand how it works | |
| 22 | 65 | $queryStatus = 'SELECT COUNT(DISTINCT mail.id) AS number, campaign.active |
| 23 | 66 | FROM #__acym_mail AS mail |
| 24 | - JOIN #__acym_queue AS queue ON queue.mail_id = mail.id | |
| 25 | 67 | LEFT JOIN #__acym_campaign AS campaign ON mail.id = campaign.mail_id'; |
| 26 | 68 | |
| 27 | 69 | if (!empty($settings['tag'])) { |
| 28 | - $query .= ' JOIN #__acym_tag AS tag ON (mail.id = tag.id_element OR mail.parent_id = tag.id_element) AND tag.type = "mail" AND tag.name = '.acym_escapeDB( | |
| 29 | - $settings['tag'] | |
| 30 | - ); | |
| 70 | + $tagJoin = ' JOIN #__acym_tag AS tag | |
| 71 | + ON (mail.id = tag.id_element OR mail.parent_id = tag.id_element) | |
| 72 | + AND tag.type = "mail" | |
| 73 | + AND tag.name = '.acym_escapeDB($settings['tag']); | |
| 74 | + $query .= $tagJoin; | |
| 75 | + $queryCount .= $tagJoin; | |
| 31 | 76 | $queryStatus .= ' JOIN #__acym_tag AS tag ON mail.id = tag.id_element AND tag.type = "mail" AND tag.name = '.acym_escapeDB($settings['tag']); |
| 32 | 77 | } |
| 33 | 78 | |
| 34 | 79 | $filters = []; |
| @@ -37,11 +82,9 @@ | ||
| 37 | 82 | if (!empty($settings['search'])) { |
| 38 | 83 | $filters[] = 'mail.subject LIKE '.acym_escapeDB('%'.$settings['search'].'%').' OR mail.name LIKE '.acym_escapeDB('%'.$settings['search'].'%'); |
| 39 | 84 | } |
| 40 | 85 | |
| 41 | - if (!empty($filters)) { | |
| 42 | - $queryStatus .= ' WHERE ('.implode(') AND (', $filters).')'; | |
| 43 | - } | |
| 86 | + $queryStatus .= ' WHERE ('.implode(') AND (', $filters).') AND mail.id IN ('.implode(',', $queuedMails).')'; | |
| 44 | 87 | |
| 45 | 88 | if (!empty($settings['status'])) { |
| 46 | 89 | $allowedStatus = [ |
| 47 | 90 | 'sending' => 'campaign.active = 1', |
| @@ -50,31 +93,28 @@ | ||
| 50 | 93 | 'followup' => 'mail.type = '.acym_escapeDB(MailClass::TYPE_FOLLOWUP), |
| 51 | 94 | ]; |
| 52 | 95 | |
| 53 | 96 | if (empty($allowedStatus[$settings['status']])) { |
| 54 | - die('Unauthorized filter: '.acym_escape($settings['status'])); | |
| 97 | + die('Unauthorized filter: '.esc_html($settings['status'])); | |
| 55 | 98 | } |
| 56 | 99 | |
| 57 | 100 | $filters[] = $allowedStatus[$settings['status']]; |
| 58 | 101 | } |
| 59 | 102 | |
| 60 | - if (!empty($filters)) { | |
| 61 | - $query .= ' WHERE ('.implode(') AND (', $filters).')'; | |
| 62 | - } | |
| 103 | + $queryCount .= ' WHERE ('.implode(') AND (', $filters).') AND mail.id IN ('.implode(',', $queuedMails).')'; | |
| 104 | + $query .= ' WHERE ('.implode(') AND (', $filters).')'; | |
| 105 | + $query .= ' ORDER BY queue.min_sending_date ASC'; | |
| 63 | 106 | |
| 64 | - $queryCount = 'SELECT COUNT(DISTINCT mail.id) '.$query; | |
| 65 | - $query .= ' GROUP BY mail.id'; | |
| 66 | - | |
| 67 | - $query = 'SELECT mail.name, mail.subject, mail.type, mail.id, campaign.id AS campaign, IF(campaign.sending_date IS NULL, queue.sending_date, campaign.sending_date) AS sending_date, campaign.sending_type, campaign.active, campaign.sending_params AS sending_params, COUNT(queue.mail_id) AS nbqueued, mail.language, mail.parent_id '.$query.' ORDER BY queue.sending_date ASC'; | |
| 68 | - | |
| 69 | 107 | acym_query('SET SQL_BIG_SELECTS=1;'); |
| 70 | - $results['elements'] = $mailClass->decode(acym_loadObjectList($query, '', $settings['offset'], $settings['campaignsPerPage'])); | |
| 71 | - $results['total'] = acym_loadResult($queryCount); | |
| 108 | + $results = [ | |
| 109 | + 'elements' => $mailClass->decode(acym_loadObjectList($query, '', $settings['offset'], $settings['campaignsPerPage'])), | |
| 110 | + 'total' => acym_loadObject($queryCount), | |
| 111 | + ]; | |
| 72 | 112 | |
| 73 | 113 | $isMultilingual = acym_isMultilingual(); |
| 74 | 114 | $campaignRecipientsMultilingual = []; |
| 75 | - $automationHelper = new AutomationHelper(); | |
| 76 | 115 | |
| 116 | + // Get the recipients | |
| 77 | 117 | $specialTypes = []; |
| 78 | 118 | acym_trigger('getCampaignTypes', [&$specialTypes]); |
| 79 | 119 | |
| 80 | 120 | foreach ($results['elements'] as $i => $oneMail) { |
| @@ -111,8 +151,10 @@ | ||
| 111 | 151 | $results['elements'][$i]->recipients = intval($mailStatClass->getTotalSubscribersByMailIdWithChild($mailId)); |
| 112 | 152 | } |
| 113 | 153 | } elseif ($isMultilingual) { |
| 114 | 154 | if (empty($campaignRecipientsMultilingual[$oneMail->campaign])) { |
| 155 | + $automationHelper = new AutomationHelper(); | |
| 156 | + | |
| 115 | 157 | $listIds = array_keys($results['elements'][$i]->lists); |
| 116 | 158 | acym_arrayToInteger($listIds); |
| 117 | 159 | |
| 118 | 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( |
| @@ -171,17 +213,27 @@ | ||
| 171 | 213 | ON mail.id = queue.mail_id |
| 172 | 214 | WHERE mail.type = '.acym_escapeDB(MailClass::TYPE_FOLLOWUP) |
| 173 | 215 | ); |
| 174 | 216 | |
| 175 | - $elementsPerStatus = acym_loadObjectList($queryStatus.' GROUP BY active', 'active'); | |
| 176 | - foreach ($elementsPerStatus as $i => $element) { | |
| 177 | - $elementsPerStatus[$i] = empty($element->number) ? 0 : $element->number; | |
| 217 | + $elementsPerStatus = acym_loadObjectList($queryStatus.' GROUP BY active'); | |
| 218 | + $queuedActiveCampaigns = 0; | |
| 219 | + $queuedPausedCampaigns = 0; | |
| 220 | + foreach ($elementsPerStatus as $element) { | |
| 221 | + if (is_null($element->active)) { | |
| 222 | + continue; | |
| 223 | + } | |
| 224 | + | |
| 225 | + if (!empty($element->active)) { | |
| 226 | + $queuedActiveCampaigns = intval($element->number); | |
| 227 | + } else { | |
| 228 | + $queuedPausedCampaigns = intval($element->number); | |
| 229 | + } | |
| 178 | 230 | } |
| 179 | 231 | |
| 180 | 232 | $results['status'] = [ |
| 181 | - 'all' => array_sum($elementsPerStatus) + $automationNumber + $followupNumber, | |
| 182 | - 'sending' => $elementsPerStatus[1] ?? 0, | |
| 183 | - 'paused' => $elementsPerStatus[0] ?? 0, | |
| 233 | + 'all' => $queuedActiveCampaigns + $queuedPausedCampaigns + $automationNumber + $followupNumber, | |
| 234 | + 'sending' => $queuedActiveCampaigns, | |
| 235 | + 'paused' => $queuedPausedCampaigns, | |
| 184 | 236 | 'automation' => $automationNumber, |
| 185 | 237 | 'followup' => $followupNumber, |
| 186 | 238 | ]; |
| 187 | 239 | |
| @@ -187,11 +239,13 @@ | ||
| 187 | 239 | |
| 188 | 240 | return $results; |
| 189 | 241 | } |
| 190 | 242 | |
| 191 | - public function getMatchingScheduledCampaigns($settings) | |
| 243 | + /** | |
| 244 | + * Get campaigns depending on filters (search, status, pagination) | |
| 245 | + */ | |
| 246 | + public function getMatchingScheduledCampaigns(array $settings): array | |
| 192 | 247 | { |
| 193 | - $campaignClass = new CampaignClass(); | |
| 194 | 248 | $mailClass = new MailClass(); |
| 195 | 249 | $query = 'FROM #__acym_mail AS mail |
| 196 | 250 | JOIN #__acym_campaign AS campaign ON mail.id = campaign.mail_id OR mail.parent_id = campaign.mail_id '; |
| 197 | 251 | |
| @@ -201,9 +255,9 @@ | ||
| 201 | 255 | |
| 202 | 256 | $filters = [ |
| 203 | 257 | 'campaign.draft = 0', |
| 204 | 258 | 'campaign.sent = 0', |
| 205 | - 'campaign.sending_type = '.acym_escapeDB($campaignClass->getConstScheduled()), | |
| 259 | + 'campaign.sending_type = '.acym_escapeDB(CampaignClass::SENDING_TYPE_SCHEDULED), | |
| 206 | 260 | ]; |
| 207 | 261 | |
| 208 | 262 | if (!empty($settings['search'])) { |
| 209 | 263 | $filters[] = 'mail.subject LIKE '.acym_escapeDB('%'.$settings['search'].'%').' OR mail.name LIKE '.acym_escapeDB('%'.$settings['search'].'%'); |
| @@ -208,18 +262,16 @@ | ||
| 208 | 262 | if (!empty($settings['search'])) { |
| 209 | 263 | $filters[] = 'mail.subject LIKE '.acym_escapeDB('%'.$settings['search'].'%').' OR mail.name LIKE '.acym_escapeDB('%'.$settings['search'].'%'); |
| 210 | 264 | } |
| 211 | 265 | |
| 212 | - if (!empty($filters)) { | |
| 213 | - $query .= ' WHERE ('.implode(') AND (', $filters).')'; | |
| 214 | - } | |
| 266 | + $query .= ' WHERE ('.implode(') AND (', $filters).')'; | |
| 215 | 267 | |
| 216 | - $queryCount = 'SELECT COUNT(DISTINCT mail.id) '.$query; | |
| 268 | + $queryCount = 'SELECT COUNT(DISTINCT mail.id) AS total '.$query; | |
| 217 | 269 | $query = 'SELECT mail.name, mail.subject, mail.id, campaign.sending_date, campaign.sending_params, mail.language, mail.parent_id '.$query.' GROUP BY mail.id ORDER BY campaign.sending_date ASC'; |
| 218 | 270 | |
| 219 | 271 | acym_query('SET SQL_BIG_SELECTS=1;'); |
| 220 | 272 | $results['elements'] = $mailClass->decode(acym_loadObjectList($query, '', $settings['offset'], $settings['campaignsPerPage'])); |
| 221 | - $results['total'] = acym_loadResult($queryCount); | |
| 273 | + $results['total'] = acym_loadObject($queryCount); | |
| 222 | 274 | |
| 223 | 275 | foreach ($results['elements'] as $i => $oneMail) { |
| 224 | 276 | $results['elements'][$i]->sending_params = empty($oneMail->sending_params) ? [] : json_decode($oneMail->sending_params, true); |
| 225 | 277 | $mailId = empty($oneMail->parent_id) ? $oneMail->id : $oneMail->parent_id; |
| @@ -234,9 +286,12 @@ | ||
| 234 | 286 | |
| 235 | 287 | return $results; |
| 236 | 288 | } |
| 237 | 289 | |
| 238 | - public function getMatchingResults($settings) | |
| 290 | + /** | |
| 291 | + * Get mails depending on filters (search, status, pagination) | |
| 292 | + */ | |
| 293 | + public function getMatchingResults(array $settings): array | |
| 239 | 294 | { |
| 240 | 295 | $query = 'FROM #__acym_queue AS queue |
| 241 | 296 | JOIN #__acym_mail AS mail ON mail.id = queue.mail_id |
| 242 | 297 | JOIN #__acym_user AS user ON queue.user_id = user.id '; |
| @@ -261,27 +316,29 @@ | ||
| 261 | 316 | if (!empty($filters)) { |
| 262 | 317 | $query .= ' WHERE ('.implode(') AND (', $filters).')'; |
| 263 | 318 | } |
| 264 | 319 | |
| 265 | - 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; | |
| 266 | 324 | $query .= ' GROUP BY queue.mail_id, queue.user_id'; |
| 267 | 325 | } |
| 268 | 326 | |
| 269 | - $queryCount = 'SELECT COUNT(queue.mail_id) '.$query; | |
| 270 | 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'; |
| 271 | 328 | |
| 272 | 329 | $mailClass = new MailClass(); |
| 273 | - $results['elements'] = $mailClass->decode(acym_loadObjectList($query, '', $settings['offset'], $settings['elementsPerPage'])); | |
| 274 | - $results['total'] = acym_loadResult($queryCount); | |
| 275 | 330 | |
| 276 | - return $results; | |
| 331 | + return [ | |
| 332 | + 'elements' => $mailClass->decode(acym_loadObjectList($query, '', $settings['offset'], $settings['elementsPerPage'])), | |
| 333 | + 'total' => acym_loadObject($queryCount), | |
| 334 | + ]; | |
| 277 | 335 | } |
| 278 | 336 | |
| 279 | - public function scheduleReady() | |
| 337 | + public function scheduleReady(): ?int | |
| 280 | 338 | { |
| 281 | 339 | $this->messages = []; |
| 282 | 340 | |
| 283 | - $campaignClass = new CampaignClass(); | |
| 284 | 341 | $mailClass = new MailClass(); |
| 285 | 342 | |
| 286 | 343 | $multilingualQuery = acym_isMultilingual() ? ' OR mail.parent_id = campaign.mail_id ' : ''; |
| 287 | 344 | |
| @@ -290,9 +347,9 @@ | ||
| 290 | 347 | 'SELECT mail.id, campaign.sending_date, mail.name, campaign.mail_id AS parent_id, mail.language, campaign.sending_params |
| 291 | 348 | FROM #__acym_campaign AS campaign |
| 292 | 349 | JOIN #__acym_mail AS mail |
| 293 | 350 | ON campaign.mail_id = mail.id '.$multilingualQuery.' |
| 294 | - WHERE campaign.sending_type = '.acym_escapeDB($campaignClass->getConstScheduled()).' | |
| 351 | + WHERE campaign.sending_type = '.acym_escapeDB(CampaignClass::SENDING_TYPE_SCHEDULED).' | |
| 295 | 352 | AND campaign.draft = 0 |
| 296 | 353 | AND campaign.sending_date <= '.acym_escapeDB(acym_date('now', 'Y-m-d H:i:s', false)).' |
| 297 | 354 | AND campaign.sent = 0', |
| 298 | 355 | 'id' |
| @@ -299,16 +356,26 @@ | ||
| 299 | 356 | ) |
| 300 | 357 | ); |
| 301 | 358 | |
| 302 | 359 | if (empty($mailReady)) { |
| 303 | - return false; | |
| 360 | + return null; | |
| 304 | 361 | } |
| 305 | 362 | |
| 306 | 363 | $nbQueue = []; |
| 307 | 364 | |
| 308 | 365 | foreach ($mailReady as $mailId => $mail) { |
| 309 | - $nbQueue[$mailId] = $this->queue($mail); | |
| 310 | - $this->messages[] = acym_translationSprintf('ACYM_ADDED_QUEUE_SCHEDULE', $nbQueue[$mailId], '<b>'.$mail->name.'</b>'); | |
| 366 | + // A/B test campaigns are queued later by CampaignClass::send(), so skip queueing here | |
| 367 | + $sendingParams = $mail->sending_params ?? []; | |
| 368 | + if (is_string($sendingParams)) { | |
| 369 | + $sendingParams = json_decode($sendingParams, true) ?? []; | |
| 370 | + } | |
| 371 | + | |
| 372 | + if (!empty($sendingParams['abtest']['repartition'])) { | |
| 373 | + $nbQueue[$mailId] = 0; | |
| 374 | + } else { | |
| 375 | + $nbQueue[$mailId] = $this->queue($mail); | |
| 376 | + $this->messages[] = acym_translationSprintf('ACYM_ADDED_QUEUE_SCHEDULE', $nbQueue[$mailId], '<b>'.$mail->name.'</b>'); | |
| 377 | + } | |
| 311 | 378 | } |
| 312 | 379 | |
| 313 | 380 | $mailIds = array_keys($mailReady); |
| 314 | 381 | acym_arrayToInteger($mailIds); |
| @@ -315,14 +382,20 @@ | ||
| 315 | 382 | $campaigns = acym_loadObjectList('SELECT id, mail_id FROM #__acym_campaign WHERE mail_id IN ('.implode(',', $mailIds).')'); |
| 316 | 383 | $campaignClass = new CampaignClass(); |
| 317 | 384 | foreach ($campaigns as $campaign) { |
| 318 | 385 | $result = $campaignClass->send($campaign->id, $nbQueue[$campaign->mail_id]); |
| 319 | - if (empty($result) && acym_isMultilingual()) { | |
| 320 | - $translatedMails = acym_loadResultArray('SELECT id FROM #__acym_mail WHERE id != '.intval($campaign->mail_id).' AND parent_id = '.intval($campaign->mail_id)); | |
| 321 | - if (!empty($translatedMails)) { | |
| 322 | - foreach ($translatedMails as $translatedMailId) { | |
| 323 | - if (!empty($nbQueue[$translatedMailId])) $campaignClass->send($campaign->id, $nbQueue[$translatedMailId]); | |
| 324 | - } | |
| 386 | + if (!empty($result) || !acym_isMultilingual()) { | |
| 387 | + continue; | |
| 388 | + } | |
| 389 | + | |
| 390 | + $translatedMails = acym_loadResultArray('SELECT id FROM #__acym_mail WHERE id != '.intval($campaign->mail_id).' AND parent_id = '.intval($campaign->mail_id)); | |
| 391 | + if (empty($translatedMails)) { | |
| 392 | + continue; | |
| 393 | + } | |
| 394 | + | |
| 395 | + foreach ($translatedMails as $translatedMailId) { | |
| 396 | + if (!empty($nbQueue[$translatedMailId])) { | |
| 397 | + $campaignClass->send($campaign->id, $nbQueue[$translatedMailId]); | |
| 325 | 398 | } |
| 326 | 399 | } |
| 327 | 400 | } |
| 328 | 401 | |
| @@ -328,12 +401,11 @@ | ||
| 328 | 401 | |
| 329 | 402 | return count($mailReady); |
| 330 | 403 | } |
| 331 | 404 | |
| 332 | - public function delete($elements) | |
| 405 | + public function delete(array $elements): int | |
| 333 | 406 | { |
| 334 | 407 | if (empty($elements)) return 0; |
| 335 | - if (!is_array($elements)) $elements = [$elements]; | |
| 336 | 408 | acym_arrayToInteger($elements); |
| 337 | 409 | |
| 338 | 410 | $query = 'DELETE FROM #__acym_queue WHERE mail_id IN ('.implode(',', $elements).')'; |
| 339 | 411 | $result = acym_query($query); |
| @@ -340,60 +412,48 @@ | ||
| 340 | 412 | |
| 341 | 413 | acym_query('UPDATE #__acym_campaign SET draft = 1, active = 1 WHERE mail_id IN ('.implode(',', $elements).')'); |
| 342 | 414 | |
| 343 | 415 | if (!$result) { |
| 344 | - return false; | |
| 416 | + return 0; | |
| 345 | 417 | } |
| 346 | 418 | |
| 347 | 419 | return $result; |
| 348 | 420 | } |
| 349 | 421 | |
| 350 | - public function deleteOne($elements, $mailId = null) | |
| 422 | + public function deleteQueuedByUserIds(array $userIds, ?int $mailId = null): int | |
| 351 | 423 | { |
| 352 | - if (!is_array($elements)) { | |
| 353 | - $elements = [$elements]; | |
| 424 | + acym_arrayToInteger($userIds); | |
| 425 | + if (empty($userIds)) { | |
| 426 | + return 0; | |
| 354 | 427 | } |
| 355 | 428 | |
| 356 | - if (empty($elements)) { | |
| 357 | - return 0; | |
| 429 | + $query = 'DELETE FROM #__acym_queue WHERE user_id IN ('.implode(',', $userIds).')'; | |
| 430 | + if (!empty($mailId)) { | |
| 431 | + $query .= ' AND mail_id = '.intval($mailId); | |
| 358 | 432 | } |
| 359 | 433 | |
| 360 | - $nbDeleted = 0; | |
| 361 | - foreach ($elements as $one) { | |
| 362 | - if (strpos($one, '_')) { | |
| 363 | - [$mailId, $userId] = explode('_', $one); | |
| 364 | - } else { | |
| 365 | - $userId = $one; | |
| 434 | + try { | |
| 435 | + $nbDeleted = acym_query($query); | |
| 436 | + | |
| 437 | + if ($nbDeleted === false) { | |
| 438 | + $this->errors[] = acym_getDBError(); | |
| 439 | + | |
| 440 | + return 0; | |
| 366 | 441 | } |
| 367 | 442 | |
| 368 | - $query = 'DELETE FROM #__acym_queue WHERE user_id = '.intval($userId); | |
| 369 | - if (!empty($mailId)) { | |
| 370 | - $query .= ' AND mail_id = '.intval($mailId); | |
| 371 | - } | |
| 443 | + return (int)$nbDeleted; | |
| 444 | + } catch (\Exception $e) { | |
| 445 | + $this->errors[] = $e->getMessage(); | |
| 372 | 446 | |
| 373 | - try { | |
| 374 | - $res = acym_query($query); | |
| 375 | - } catch (\Exception $e) { | |
| 376 | - $res = false; | |
| 377 | - } | |
| 378 | - if ($res === false) { | |
| 379 | - if (isset($e)) { | |
| 380 | - $this->errors[] = $e->getMessage(); | |
| 381 | - unset($e); | |
| 382 | - } else { | |
| 383 | - $this->errors[] = acym_getDBError(); | |
| 384 | - } | |
| 385 | - } else { | |
| 386 | - $nbDeleted += $res; | |
| 387 | - } | |
| 447 | + return 0; | |
| 388 | 448 | } |
| 389 | - | |
| 390 | - return $res; | |
| 391 | 449 | } |
| 392 | 450 | |
| 393 | - public function getReady($limit, $mailId = 0): array | |
| 451 | + public function getReady(int $startFrom, int $limit, int $mailId = 0): array | |
| 394 | 452 | { |
| 395 | - if (empty($limit)) return []; | |
| 453 | + if (empty($limit)) { | |
| 454 | + return []; | |
| 455 | + } | |
| 396 | 456 | |
| 397 | 457 | $query = 'SELECT queue.*, campaign.sending_params AS sending_params FROM #__acym_queue AS queue'; |
| 398 | 458 | $query .= ' JOIN #__acym_user AS user ON queue.`user_id` = user.`id` '; |
| 399 | 459 | $query .= ' JOIN #__acym_mail AS mail ON queue.`mail_id` = mail.`id` '; |
| @@ -420,8 +480,9 @@ | ||
| 420 | 480 | if (!empty($mailId)) { |
| 421 | 481 | $query .= ' AND queue.`mail_id` = '.intval($mailId); |
| 422 | 482 | } |
| 423 | 483 | |
| 484 | + // We don't display this option in the configuration anymore but we use its value if it's set in the database | |
| 424 | 485 | $sendOrder = $this->config->get('sendorder'); |
| 425 | 486 | if (empty($sendOrder)) { |
| 426 | 487 | $order = 'queue.`user_id` ASC'; |
| 427 | 488 | } elseif ($sendOrder === 'rand') { |
| @@ -428,14 +489,14 @@ | ||
| 428 | 489 | $order = 'RAND()'; |
| 429 | 490 | } else { |
| 430 | 491 | $sendOrder = str_replace('subid', 'user_id', $sendOrder); |
| 431 | 492 | $ordering = explode(',', $sendOrder); |
| 432 | - $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')); | |
| 433 | 494 | } |
| 434 | 495 | |
| 435 | 496 | $query .= ' ORDER BY queue.`priority` ASC, queue.`sending_date` ASC, '.$order; |
| 436 | - $startqueue = acym_getVar('int', 'startqueue', 0); | |
| 437 | - $query .= ' LIMIT '.intval($startqueue).','.intval($limit); | |
| 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... | |
| 498 | + $query .= ' LIMIT '.intval($startFrom).','.intval($limit); | |
| 438 | 499 | |
| 439 | 500 | try { |
| 440 | 501 | $results = acym_loadObjectList($query); |
| 441 | 502 | } catch (\Exception $e) { |
| @@ -442,8 +503,9 @@ | ||
| 442 | 503 | $results = null; |
| 443 | 504 | } |
| 444 | 505 | |
| 445 | 506 | if ($results === null) { |
| 507 | + // We got an issue here... maybe the table is crashed so we will repair it. | |
| 446 | 508 | acym_query('REPAIR TABLE #__acym_queue, #__acym_user, #__acym_mail, #__acym_campaign'); |
| 447 | 509 | } |
| 448 | 510 | |
| 449 | 511 | if (empty($results)) { |
| @@ -449,8 +511,12 @@ | ||
| 449 | 511 | if (empty($results)) { |
| 450 | 512 | return []; |
| 451 | 513 | } |
| 452 | 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 | |
| 453 | 519 | $firstElementQueued = reset($results); |
| 454 | 520 | acym_query( |
| 455 | 521 | 'UPDATE #__acym_queue |
| 456 | 522 | SET sending_date = DATE_ADD(sending_date, INTERVAL 1 SECOND) |
| @@ -460,16 +526,16 @@ | ||
| 460 | 526 | |
| 461 | 527 | return $results; |
| 462 | 528 | } |
| 463 | 529 | |
| 464 | - public function delayFailed($mailId, $userIds) | |
| 530 | + public function delayFailed(int $mailId, array $userIds): void | |
| 465 | 531 | { |
| 466 | 532 | acym_arrayToInteger($userIds); |
| 467 | 533 | if (empty($mailId) || empty($userIds)) { |
| 468 | - return false; | |
| 534 | + return; | |
| 469 | 535 | } |
| 470 | 536 | |
| 471 | - return acym_query( | |
| 537 | + acym_query( | |
| 472 | 538 | 'UPDATE #__acym_queue |
| 473 | 539 | SET sending_date = DATE_ADD(sending_date, INTERVAL 1 HOUR), try = try +1 |
| 474 | 540 | WHERE mail_id = '.intval($mailId).' |
| 475 | 541 | AND user_id IN ('.implode(',', $userIds).')' |
| @@ -475,15 +541,15 @@ | ||
| 475 | 541 | AND user_id IN ('.implode(',', $userIds).')' |
| 476 | 542 | ); |
| 477 | 543 | } |
| 478 | 544 | |
| 479 | - public function delayAll(int $hours) | |
| 545 | + public function delayAll(int $hours): void | |
| 480 | 546 | { |
| 481 | 547 | if ($hours < 1) { |
| 482 | - return false; | |
| 548 | + return; | |
| 483 | 549 | } |
| 484 | 550 | |
| 485 | - return acym_query( | |
| 551 | + acym_query( | |
| 486 | 552 | 'UPDATE #__acym_queue |
| 487 | 553 | SET sending_date = DATE_ADD(sending_date, INTERVAL '.$hours.' HOUR) |
| 488 | 554 | WHERE sending_date < DATE_ADD(NOW(), INTERVAL '.intval($hours).' HOUR)' |
| 489 | 555 | ); |
| @@ -488,9 +554,9 @@ | ||
| 488 | 554 | WHERE sending_date < DATE_ADD(NOW(), INTERVAL '.intval($hours).' HOUR)' |
| 489 | 555 | ); |
| 490 | 556 | } |
| 491 | 557 | |
| 492 | - public function getMailReceivers($mail, $onlyNew = false) | |
| 558 | + public function getMailReceivers(object $mail, bool $onlyNew = false): AutomationHelper | |
| 493 | 559 | { |
| 494 | 560 | if (empty($mail->sending_params)) { |
| 495 | 561 | $sendingParams = []; |
| 496 | 562 | $mail->filters = []; |
| @@ -512,11 +578,13 @@ | ||
| 512 | 578 | 'userlist.status = 1', |
| 513 | 579 | 'maillist.mail_id = '.intval(empty($mail->parent_id) ? $mail->id : $mail->parent_id), |
| 514 | 580 | ]; |
| 515 | 581 | |
| 582 | + // Send this version only to the users with the correct language | |
| 516 | 583 | if (!$onlyNew && acym_isMultilingual()) { |
| 517 | 584 | $where = 'user.language = '.acym_escapeDB($mail->language); |
| 518 | 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 | |
| 519 | 587 | $where .= ' OR user.language = "" OR user.language NOT IN (SELECT language FROM #__acym_mail WHERE parent_id = '.intval($mail->id).')'; |
| 520 | 588 | } |
| 521 | 589 | $automationHelper->where[] = $where; |
| 522 | 590 | } |
| @@ -530,8 +598,9 @@ | ||
| 530 | 598 | $mail->id |
| 531 | 599 | ).' OR id = '.intval($mail->id).')'; |
| 532 | 600 | $automationHelper->where[] = '`us`.`user_id` IS NULL'; |
| 533 | 601 | |
| 602 | + // Do not count the disabled user for the resend counter on summary | |
| 534 | 603 | $automationHelper->where[] = '`user`.`active` = 1'; |
| 535 | 604 | } |
| 536 | 605 | |
| 537 | 606 | $automationHelper->removeFlag(SegmentsController::FLAG_USERS); |
| @@ -536,12 +605,14 @@ | ||
| 536 | 605 | |
| 537 | 606 | $automationHelper->removeFlag(SegmentsController::FLAG_USERS); |
| 538 | 607 | $automationHelper->removeFlag(SegmentsController::FLAG_COUNT); |
| 539 | 608 | |
| 609 | + // Handle potential segment | |
| 540 | 610 | if (empty($mail->filters)) { |
| 541 | 611 | return $automationHelper; |
| 542 | 612 | } |
| 543 | 613 | |
| 614 | + // Mark users matching the segment | |
| 544 | 615 | foreach ($mail->filters as $orValues) { |
| 545 | 616 | if (empty($orValues)) continue; |
| 546 | 617 | |
| 547 | 618 | $automationHelperSegment = new AutomationHelper(); |
| @@ -562,11 +633,12 @@ | ||
| 562 | 633 | |
| 563 | 634 | return $automationHelper; |
| 564 | 635 | } |
| 565 | 636 | |
| 566 | - public function queue($mail) | |
| 637 | + public function queue(object $mail): int | |
| 567 | 638 | { |
| 568 | 639 | $automationHelper = $this->getMailReceivers($mail); |
| 640 | + // Only queue enabled users | |
| 569 | 641 | $automationHelper->where[] = '`user`.`active` = 1'; |
| 570 | 642 | |
| 571 | 643 | $priority = $this->config->get('priority_newsletter', 3); |
| 572 | 644 | $select = [intval($mail->id), 'userlist.user_id', acym_escapeDB($mail->sending_date), intval($priority), '0']; |
| @@ -573,19 +645,23 @@ | ||
| 573 | 645 | $inserted = acym_query('INSERT IGNORE INTO #__acym_queue (`mail_id`, `user_id`, `sending_date`, `priority`, `try`) ('.$automationHelper->getQuery($select).')'); |
| 574 | 646 | |
| 575 | 647 | $automationHelper->removeFlag(SegmentsController::FLAG_COUNT); |
| 576 | 648 | |
| 577 | - return $inserted; | |
| 649 | + return (int)$inserted; | |
| 578 | 650 | } |
| 579 | 651 | |
| 580 | - public function addQueue($userId, $mailId, $sendingDate) | |
| 652 | + public function addQueue(int $userId, int $mailId, string $sendingDate): int | |
| 581 | 653 | { |
| 582 | 654 | $priority = $this->config->get('priority_newsletter', 3); |
| 583 | 655 | |
| 584 | - return acym_query('INSERT IGNORE INTO #__acym_queue (`mail_id`, `user_id`, `sending_date`, `priority`, `try`) VALUES ('.intval($mailId).', '.intval($userId).', '.acym_escapeDB($sendingDate).', '.intval($priority).', 0)'); | |
| 656 | + return (int)acym_query( | |
| 657 | + 'INSERT IGNORE INTO #__acym_queue (`mail_id`, `user_id`, `sending_date`, `priority`, `try`) VALUES ('.intval($mailId).', '.intval($userId).', '.acym_escapeDB( | |
| 658 | + $sendingDate | |
| 659 | + ).', '.intval($priority).', 0)' | |
| 660 | + ); | |
| 585 | 661 | } |
| 586 | 662 | |
| 587 | - public function unpauseCampaign($campaignId, $active) | |
| 663 | + public function unpauseCampaign(int $campaignId, int $active): void | |
| 588 | 664 | { |
| 589 | 665 | if (acym_query('UPDATE #__acym_campaign SET active = '.intval($active).' WHERE id = '.intval($campaignId))) { |
| 590 | 666 | acym_enqueueMessage(acym_translation($active ? 'ACYM_UNPAUSE_CAMPAIGN_SUCCESSFUL' : 'ACYM_PAUSE_CAMPAIGN_SUCCESSFUL'), "success"); |
| 591 | 667 | } else { |
| @@ -592,19 +668,21 @@ | ||
| 592 | 668 | acym_enqueueMessage(acym_translation($active ? 'ACYM_UNPAUSE_CAMPAIGN_FAIL' : 'ACYM_PAUSE_CAMPAIGN_FAIL'), "error"); |
| 593 | 669 | } |
| 594 | 670 | } |
| 595 | 671 | |
| 596 | - public function emptyQueue() | |
| 672 | + public function emptyQueue(): int | |
| 597 | 673 | { |
| 598 | - return acym_query('DELETE FROM `#__acym_queue`'); | |
| 674 | + return (int)acym_query('DELETE FROM `#__acym_queue`'); | |
| 599 | 675 | } |
| 600 | 676 | |
| 601 | - public function cleanQueue() | |
| 677 | + public function cleanQueue(): int | |
| 602 | 678 | { |
| 603 | 679 | $twoDaysEarlier = acym_date(time() - 172800, 'Y-m-d H:i:s', false); |
| 604 | 680 | |
| 605 | 681 | $conditionUser = '`user`.`active` = 0'; |
| 606 | - if ($this->config->get('require_confirmation', 1) == 1) $conditionUser .= ' OR `user`.`confirmed` = 0'; | |
| 682 | + if ($this->config->get('require_confirmation', 1) == 1) { | |
| 683 | + $conditionUser .= ' OR `user`.`confirmed` = 0'; | |
| 684 | + } | |
| 607 | 685 | |
| 608 | 686 | $numberOfDaysToWait = $this->config->get('queue_delete_days', 0); |
| 609 | 687 | $conditionDateDelete = ''; |
| 610 | 688 | if (!empty($numberOfDaysToWait)) { |
| @@ -611,9 +689,9 @@ | ||
| 611 | 689 | $dateTimeConditionDelete = acym_date(time() - ($numberOfDaysToWait * 86400), 'Y-m-d H:i:s', false); |
| 612 | 690 | $conditionDateDelete = ' OR (`queue`.`sending_date` < '.acym_escapeDB($dateTimeConditionDelete).')'; |
| 613 | 691 | } |
| 614 | 692 | |
| 615 | - return acym_query( | |
| 693 | + return (int)acym_query( | |
| 616 | 694 | 'DELETE `queue`.* |
| 617 | 695 | FROM `#__acym_queue` AS `queue` |
| 618 | 696 | JOIN `#__acym_user` AS `user` ON `queue`.`user_id` = `user`.`id` |
| 619 | 697 | WHERE (('.$conditionUser.') AND `queue`.`sending_date` < '.acym_escapeDB($twoDaysEarlier).') '.$conditionDateDelete |
| @@ -619,9 +697,9 @@ | ||
| 619 | 697 | WHERE (('.$conditionUser.') AND `queue`.`sending_date` < '.acym_escapeDB($twoDaysEarlier).') '.$conditionDateDelete |
| 620 | 698 | ); |
| 621 | 699 | } |
| 622 | 700 | |
| 623 | - public function isSendingFinished($mailId): bool | |
| 701 | + public function isSendingFinished(int $mailId): bool | |
| 624 | 702 | { |
| 625 | 703 | $mailClass = new MailClass(); |
| 626 | 704 | $mail = $mailClass->getOneById($mailId); |
| 627 | 705 | |