PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.0.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.0.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
acymailing / back / Classes / CampaignClass.php

CampaignClass.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 10.0.1, at back/Classes/CampaignClass.php

1,262 lines 49.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Classes;
4
5 use AcyMailing\Controllers\SegmentsController;
6 use AcyMailing\Helpers\AutomationHelper;
7 use AcyMailing\Helpers\PaginationHelper;
8 use AcyMailing\Core\AcymClass;
9
10 class CampaignClass extends AcymClass
11 {
12 const SENDING_TYPE_NOW = 'now';
13 const SENDING_TYPE_SCHEDULED = 'scheduled';
14 const SENDING_TYPE_AUTO = 'auto';
15 const SENDING_TYPES = [
16 self::SENDING_TYPE_NOW,
17 self::SENDING_TYPE_SCHEDULED,
18 self::SENDING_TYPE_AUTO,
19 ];
20 const AB_TEST_VERSIONS = ['B'];
21
22 private array $jsonColumns = ['sending_params'];
23
24 public function __construct()
25 {
26 parent::__construct();
27
28 $this->table = 'campaign';
29 $this->pkey = 'id';
30 }
31
32 public function getConstNow()
33 {
34 return self::SENDING_TYPE_NOW;
35 }
36
37 public function getConstScheduled()
38 {
39 return self::SENDING_TYPE_SCHEDULED;
40 }
41
42 public function getConstAuto()
43 {
44 return self::SENDING_TYPE_AUTO;
45 }
46
47 public function decode($campaign, $decodeMail = true)
48 {
49 if (empty($campaign)) return $campaign;
50
51 if (is_array($campaign)) {
52 foreach ($campaign as $i => $oneCampaign) {
53 $campaign[$i] = $this->decode($oneCampaign, false);
54 }
55 }
56
57 foreach ($this->jsonColumns as $oneColumn) {
58 if (!isset($campaign->$oneColumn)) continue;
59
60 $campaign->$oneColumn = empty($campaign->$oneColumn) ? [] : json_decode($campaign->$oneColumn, true);
61 }
62
63 if ($decodeMail) {
64 $mailClass = new MailClass();
65 $campaign = $mailClass->decode($campaign);
66 }
67
68 return $campaign;
69 }
70
71 public function getAll($key = null)
72 {
73 $allCampaigns = parent::getAll($key);
74
75 return $this->decode($allCampaigns);
76 }
77
78 public function getMatchingElements(array $settings = []): array
79 {
80 $tagClass = new TagClass();
81 $mailClass = new MailClass();
82
83 $query = 'SELECT campaign.*, mail.name, SUM(mail_stat.sent) AS subscribers, mail_stat.open_unique, mail_stat.tracking_sale, mail_stat.currency FROM #__acym_campaign AS campaign';
84 $queryCount = 'SELECT campaign.* FROM #__acym_campaign AS campaign';
85
86
87 $filters = [];
88 $task = acym_getVar('string', 'task');
89 if ($task === 'campaigns') {
90 $filters[] = 'mail.type = \'standard\'';
91 } elseif ($task === 'mailbox_action') {
92 $filters[] = 'mail.type = \'mailbox_action\'';
93 }
94 $mailIds = [];
95
96 $query .= ' JOIN #__acym_mail AS mail ON campaign.mail_id = mail.id';
97 $queryCount .= ' JOIN #__acym_mail AS mail ON campaign.mail_id = mail.id';
98 $query .= ' LEFT JOIN #__acym_mail_stat AS mail_stat ON campaign.mail_id = mail_stat.mail_id';
99
100 if (!acym_isAdmin()) {
101 $condition = 'mail.creator_id = '.intval(acym_currentUserId());
102 if ($this->config->get('front_campaigns_filter', 'own') === 'allowed') {
103 $listClass = new ListClass();
104 $allowedLists = $listClass->getManageableLists();
105 if (!empty($allowedLists)) {
106 $query .= ' LEFT JOIN #__acym_mail_has_list AS mailList ON campaign.mail_id = mailList.mail_id AND mailList.list_id IN ('.implode(',', $allowedLists).')';
107 $queryCount .= ' LEFT JOIN #__acym_mail_has_list AS mailList ON campaign.mail_id = mailList.mail_id AND mailList.list_id IN ('.implode(',', $allowedLists).')';
108 $condition .= ' OR mailList.list_id IS NOT NULL';
109 }
110 }
111 $filters[] = $condition;
112 $filters[] = 'campaign.sending_params NOT LIKE "%abtest%"';
113 }
114
115 if (!empty($settings['tag'])) {
116 $tagJoin = ' JOIN #__acym_tag AS tag ON campaign.mail_id = tag.id_element';
117 $query .= $tagJoin;
118 $queryCount .= $tagJoin;
119 $filters[] = 'tag.name = '.acym_escapeDB($settings['tag']);
120 $filters[] = 'tag.type = "mail"';
121 }
122
123 if (!empty($settings['search'])) {
124 $filters[] = 'mail.name LIKE '.acym_escapeDB('%'.acym_utf8Encode($settings['search']).'%');
125 }
126
127 if ($settings['status'] !== 'generated') {
128 $operator = $settings['element_tab'] === 'campaigns_auto' ? '=' : '!=';
129 if ($settings['element_tab'] === 'campaigns_auto') {
130 $filters[] = 'campaign.sending_type '.$operator.' '.acym_escapeDB(self::SENDING_TYPE_AUTO);
131 } elseif ($settings['element_tab'] === 'campaigns') {
132 $filters[] = 'campaign.sending_type IN ('.acym_escapeDB(self::SENDING_TYPE_NOW).', '.acym_escapeDB(self::SENDING_TYPE_SCHEDULED).')';
133 } elseif (!empty($settings['element_tab'])) {
134 acym_trigger('onAcymCampaignAddFiltersSpecificListing', [&$filters, $settings['element_tab']]);
135 }
136 }
137
138 if (!empty($filters)) {
139 $query .= ' WHERE ('.implode(') AND (', $filters).')';
140 $queryCount .= ' WHERE ('.implode(') AND (', $filters).')';
141 }
142
143 $statusRequests = [
144 'all' => '(campaign.parent_id IS NULL OR campaign.parent_id = 0)',
145 'scheduled' => 'campaign.sending_type = '.acym_escapeDB(self::SENDING_TYPE_SCHEDULED).' AND (campaign.parent_id IS NULL OR campaign.parent_id = 0)',
146 'sent' => 'campaign.sent = 1 AND (campaign.parent_id IS NULL OR campaign.parent_id = 0)',
147 'draft' => 'campaign.draft = 1 AND (campaign.parent_id IS NULL OR campaign.parent_id = 0)',
148 ];
149
150 if ($settings['element_tab'] == 'campaigns_auto') {
151 $statusRequests['generated'] = 'campaign.sending_type = '.acym_escapeDB(self::SENDING_TYPE_NOW).' AND campaign.parent_id > 0';
152 }
153
154 if (empty($settings['status'])) $settings['status'] = 'all';
155 if (!empty($statusRequests[$settings['status']])) {
156 $query .= empty($filters) ? ' WHERE ' : ' AND ';
157 $query .= $statusRequests[$settings['status']];
158 }
159
160 $query .= ' GROUP BY campaign.id';
161
162 if (!empty($settings['ordering']) && !empty($settings['ordering_sort_order'])) {
163 $table = in_array($settings['ordering'], ['name', 'creation_date']) ? 'mail' : 'campaign';
164 $query .= ' ORDER BY '.$table.'.'.acym_secureDBColumn($settings['ordering']).' '.acym_secureDBColumn(strtoupper($settings['ordering_sort_order']));
165 }
166
167 if (empty($settings['offset']) || $settings['offset'] < 0) {
168 $settings['offset'] = 0;
169 }
170
171 if (empty($settings['elementsPerPage']) || $settings['elementsPerPage'] < 1) {
172 $pagination = new PaginationHelper();
173 $settings['elementsPerPage'] = $pagination->getListLimit();
174 }
175
176 $results['elements'] = $this->decode(acym_loadObjectList($query, '', $settings['offset'], $settings['elementsPerPage']));
177
178 foreach ($results['elements'] as $oneCampaign) {
179 $mailIds[] = $oneCampaign->mail_id;
180 $oneCampaign->tags = '';
181 }
182
183 $tags = $tagClass->getAllTagsByTypeAndElementIds(TagClass::TYPE_MAIL, $mailIds);
184 $lists = $mailClass->getAllListsByMailIds($mailIds);
185
186 $isMultilingual = acym_isMultilingual();
187
188 $urlClickClass = new UrlClickClass();
189 foreach ($results['elements'] as $i => $oneCampaign) {
190 $results['elements'][$i]->tags = [];
191 $results['elements'][$i]->lists = [];
192 $results['elements'][$i]->automation_id = null;
193
194 foreach ($tags as $tag) {
195 if ($oneCampaign->id == $tag->id_element) {
196 $results['elements'][$i]->tags[] = $tag;
197 }
198 }
199
200 foreach ($lists as $list) {
201 if ($oneCampaign->mail_id == $list->mail_id) {
202 array_push($results['elements'][$i]->lists, $list);
203 }
204 }
205
206 if ($settings['element_tab'] === 'campaigns_auto' && $settings['status'] !== 'generated') {
207 $this->getStatsCampaignAuto($results['elements'][$i], $urlClickClass);
208 } else {
209 $isAbTest = isset($results['elements'][$i]->sending_params['abtest']);
210 if ($isMultilingual || $isAbTest) {
211 $this->prepareStatsCampaign($results['elements'][$i]);
212 }
213 $this->getStatsCampaign($results['elements'][$i], $urlClickClass, $isAbTest);
214 }
215 }
216
217 $results['total'] = acym_loadObjectList($queryCount);
218
219 return $results;
220 }
221
222 private function prepareStatsCampaign(&$element)
223 {
224 $query = 'SELECT SUM(mailstat.sent) AS subscribers, SUM(mailstat.open_unique) AS open_unique, SUM(mailstat.tracking_sale) AS tracking_sale
225 FROM #__acym_mail_stat AS mailstat
226 LEFT JOIN #__acym_mail AS mail ON mail.id = mailstat.mail_id WHERE mail.parent_id = '.intval($element->mail_id);
227 $stats = acym_loadObject($query);
228 $element->subscribers += $stats->subscribers;
229 $element->open_unique += $stats->open_unique;
230 $element->tracking_sale += $stats->tracking_sale;
231 }
232
233 public function getStatsCampaign(&$element, $urlClickClass, $isAbTest = false)
234 {
235 $element->open = 0;
236 if (!empty($element->subscribers)) {
237 $element->open = number_format($element->open_unique / $element->subscribers * 100, 2);
238
239 $clicksNb = $urlClickClass->getNumberUsersClicked($element->mail_id, $isAbTest);
240 $element->click = number_format($clicksNb / $element->subscribers * 100, 2);
241 }
242 }
243
244 public function getTrackingSales(&$element)
245 {
246 $element->sale = 0;
247 $element->currency = '';
248
249 if (acym_isMultilingual()) {
250 $trackingSales = acym_loadObject(
251 'SELECT SUM(tracking_sale) as sale, currency FROM #__acym_user_stat WHERE mail_id IN (SELECT id FROM #__acym_mail WHERE id = '.intval(
252 $element->mail_id
253 ).' OR parent_id = '.intval($element->mail_id).') AND currency IS NOT NULL'
254 );
255 } else {
256 $trackingSales = acym_loadObject(
257 'SELECT SUM(tracking_sale) as sale, currency FROM #__acym_user_stat WHERE mail_id = '.intval($element->mail_id).' AND currency IS NOT NULL'
258 );
259 }
260 $this->formatSaleTracking($element, $trackingSales);
261 }
262
263 private function getStatsCampaignAuto(&$element, $urlClickClass)
264 {
265 $generatedMailsStats = acym_loadObjectList(
266 'SELECT mail_stat.* FROM #__acym_mail AS mail JOIN #__acym_mail_stat AS mail_stat ON mail.id = mail_stat.mail_id WHERE mail.id IN (SELECT mail_id FROM #__acym_campaign WHERE parent_id = '.intval(
267 $element->id
268 ).')'
269 );
270 $element->open = 0;
271 $element->click = 0;
272 $element->subscribers = 0;
273 if (empty($generatedMailsStats)) return;
274
275 foreach ($generatedMailsStats as $key => $mailsStat) {
276 $element->open += $mailsStat->open_unique;
277 $element->click += $urlClickClass->getNumberUsersClicked($element->id);
278 $element->subscribers += $mailsStat->sent;
279 }
280
281 if (!empty($element->subscribers)) {
282 $element->open = number_format($element->open / $element->subscribers * 100, 2);
283 $element->click = number_format($element->click / $element->subscribers * 100, 2);
284 }
285
286 if (!acym_isTrackingSalesActive()) return;
287
288 $trackingSales = acym_loadObject(
289 'SELECT SUM(mail_stat.tracking_sale) AS sale, mail_stat.currency
290 FROM #__acym_mail_stat AS mail_stat
291 WHERE mail_id IN (SELECT mail_id FROM #__acym_campaign WHERE parent_id = '.intval($element->id).') AND currency IS NOT NULL'
292 );
293 $this->formatSaleTracking($element, $trackingSales);
294 }
295
296 private function formatSaleTracking(&$element, $trackingSales)
297 {
298 $element->sale = $trackingSales->sale;
299 if (empty($element->currency)) $element->currency = '';
300 acym_trigger('getCurrency', [&$element->currency]);
301 }
302
303 public function getOneById($id)
304 {
305 return $this->decode(acym_loadObject('SELECT campaign.* FROM #__acym_campaign AS campaign WHERE campaign.id = '.intval($id)));
306 }
307
308 public function getOneByIdWithMail($id)
309 {
310 $query = 'SELECT campaign.*, mail.name, mail.subject, mail.body, mail.from_name, mail.from_email, mail.reply_to_name, mail.reply_to_email, mail.bcc, mail.links_language, mail.tracking, mail.translation, mail.bounce_email
311 FROM #__acym_campaign AS campaign
312 JOIN #__acym_mail AS mail ON campaign.mail_id = mail.id
313 WHERE campaign.id = '.intval($id);
314
315 return $this->decode(acym_loadObject($query));
316 }
317
318 public function get($identifier, $column = 'id')
319 {
320 return $this->decode(acym_loadObject('SELECT campaign.* FROM #__acym_campaign AS campaign WHERE campaign.'.acym_secureDBColumn($column).' = '.acym_escapeDB($identifier)));
321 }
322
323 public function getAllCampaignsNameMailId()
324 {
325 $query = 'SELECT m.id, m.name
326 FROM #__acym_campaign AS c
327 LEFT JOIN #__acym_mail AS m ON c.mail_id = m.id';
328
329 return $this->decode(acym_loadObjectList($query));
330 }
331
332 public function getOneCampaignByMailId($mailId)
333 {
334 return $this->decode(acym_loadObject('SELECT * FROM #__acym_campaign WHERE mail_id = '.intval($mailId)));
335 }
336
337 public function getAutoCampaignFromGeneratedMailId($mailId)
338 {
339 $queryCampaign = 'SELECT * FROM #__acym_campaign WHERE id = (SELECT parent_id FROM #__acym_campaign WHERE mail_id = '.intval($mailId).')';
340
341 return $this->decode(acym_loadObject($queryCampaign));
342 }
343
344 public function manageListsToCampaign($listsIds, $mailId, $unselectedListIds = [])
345 {
346 if (!empty($unselectedListIds)) {
347 acym_arrayToInteger($unselectedListIds);
348 acym_query('DELETE FROM #__acym_mail_has_list WHERE mail_id = '.intval($mailId).' AND list_id IN ('.implode(', ', $unselectedListIds).')');
349 }
350
351 acym_arrayToInteger($listsIds);
352 if (empty($listsIds)) return false;
353
354 $values = [];
355 $listsIds = array_unique($listsIds);
356 foreach ($listsIds as $listId) {
357 $values[] = '('.intval($mailId).', '.intval($listId).')';
358 }
359
360 if (!empty($values)) {
361 acym_query('INSERT IGNORE INTO #__acym_mail_has_list (`mail_id`, `list_id`) VALUES '.implode(',', $values));
362 }
363
364 return true;
365 }
366
367 public function save($campaignToSave)
368 {
369 $campaign = clone $campaignToSave;
370 if (isset($campaign->tags)) {
371 $tags = $campaign->tags;
372 unset($campaign->tags);
373 }
374
375 foreach ($campaign as $oneAttribute => $value) {
376 if (in_array($oneAttribute, $this->jsonColumns)) {
377 $campaign->$oneAttribute = json_encode(empty($value) ? [] : $value);
378 } else {
379 if (empty($value)) continue;
380 $campaign->$oneAttribute = strip_tags($value);
381 }
382 }
383
384 $campaignID = parent::save($campaign);
385
386 if (!empty($campaignID) && isset($tags)) {
387 $tagClass = new TagClass();
388 $tagClass->setTags(TagClass::TYPE_MAIL, intval($campaign->mail_id), $tags);
389 }
390
391 return $campaignID;
392 }
393
394 public function onlyManageableCampaigns(&$elements)
395 {
396 if (acym_isAdmin()) return;
397
398 $idCurrentUser = acym_currentUserId();
399 if (empty($idCurrentUser)) return;
400
401 $manageable = acym_loadResultArray(
402 'SELECT campaign.id
403 FROM #__acym_campaign AS campaign
404 JOIN #__acym_mail AS mail ON campaign.mail_id = mail.id
405 WHERE mail.creator_id = '.intval($idCurrentUser)
406 );
407 $elements = array_intersect($elements, $manageable);
408 }
409
410 public function hasUserAccess(int $campaignId): bool
411 {
412 $userId = acym_currentUserId();
413 if (empty($userId)) {
414 return false;
415 }
416
417 if (acym_isAdmin()) {
418 return true;
419 }
420
421 $query = 'SELECT COUNT(*) FROM #__acym_campaign AS campaign
422 JOIN #__acym_mail AS mail ON campaign.mail_id = mail.id ';
423
424 $condition = 'mail.creator_id = '.intval($userId);
425 if ($this->config->get('front_campaigns_filter', 'own') === 'allowed') {
426 $listClass = new ListClass();
427 $allowedLists = $listClass->getManageableLists();
428 if (!empty($allowedLists)) {
429 $query .= ' LEFT JOIN #__acym_mail_has_list AS mailList ON campaign.mail_id = mailList.mail_id AND mailList.list_id IN ('.implode(',', $allowedLists).')';
430 $condition .= ' OR mailList.list_id IS NOT NULL';
431 }
432 }
433
434 $query .= 'WHERE campaign.id = '.intval($campaignId).' AND ('.$condition.')';
435
436 return acym_loadResult($query) > 0;
437 }
438
439 public function delete($elements)
440 {
441 if (!is_array($elements)) $elements = [$elements];
442 acym_arrayToInteger($elements);
443 $this->onlyManageableCampaigns($elements);
444
445 if (empty($elements)) return 0;
446
447 $mailsToDelete = [];
448 foreach ($elements as $id) {
449 $mailsToDelete[] = acym_loadResult('SELECT mail_id FROM #__acym_campaign WHERE id = '.intval($id));
450 acym_query('UPDATE #__acym_campaign SET mail_id = NULL WHERE id = '.intval($id));
451 }
452
453 $mailClass = new MailClass();
454 $mailClass->delete($mailsToDelete);
455
456 return parent::delete($elements);
457 }
458
459 public function countUsersCampaign($campaignID, $onlyActive = false): int
460 {
461 $campaign = $this->getOneById($campaignID);
462 if (empty($campaign)) {
463 return 0;
464 }
465
466 $mailClass = new MailClass();
467 $lists = $mailClass->getAllListsByMailId($campaign->mail_id);
468 $listsIds = array_column($lists, 'id');
469
470 if (empty($listsIds)) {
471 return 0;
472 }
473
474 $automationHelperBase = new AutomationHelper();
475 $automationHelperBase->removeFlag(SegmentsController::FLAG_COUNT);
476
477 $exclude = !empty($campaign->sending_params['segment']['invert']) && $campaign->sending_params['segment']['invert'] === 'exclude';
478
479 $automationHelpers = [];
480 $filters = $this->getFilterCampaign($campaign->sending_params);
481 foreach ($filters as $or => $orValues) {
482 if (empty($orValues)) continue;
483
484 $automationHelpers[$or] = new AutomationHelper();
485 foreach ($orValues as $and => $andValues) {
486 $and = intval($and);
487 foreach ($andValues as $filterName => $options) {
488 acym_trigger('onAcymProcessFilter_'.$filterName, [&$automationHelpers[$or], &$options, &$and]);
489 }
490 }
491
492 $automationHelpers[$or]->excludeSelected = $exclude;
493 $automationHelpers[$or]->addFlag(SegmentsController::FLAG_COUNT);
494 }
495
496 $join = ' #__acym_user_has_list AS user_list ON user_list.user_id = user.id ';
497 $join .= 'AND user_list.list_id IN ('.implode(',', $listsIds).') ';
498 $join .= 'AND user_list.status = 1 ';
499 if ($this->config->get('require_confirmation', 1) == 1) {
500 $join .= 'AND user.confirmed = 1 ';
501 }
502 if ($onlyActive) {
503 $join .= 'AND user.active = 1 ';
504 }
505
506 if (empty($automationHelpers)) {
507 $automationHelperBase->join['user_list'] = $join;
508
509 return acym_loadResult($automationHelperBase->getQuery(['COUNT(DISTINCT user.id)']));
510 } else {
511 $automationHelperBase = array_pop($automationHelpers);
512 $automationHelperBase->join['user_list'] = $join;
513 $numberOfRecipients = acym_loadResult($automationHelperBase->getQuery(['COUNT(DISTINCT user.id)']));
514 $automationHelperBase->removeFlag(SegmentsController::FLAG_COUNT);
515
516 return $numberOfRecipients;
517 }
518 }
519
520 public function getFilterCampaign($sendingParams)
521 {
522 $filters = [];
523
524 if (!empty($sendingParams['segment'])) {
525 if (!empty($sendingParams['segment']['filters'])) {
526 $filters = $sendingParams['segment']['filters'];
527 } elseif (!empty($sendingParams['segment']['segment_id'])) {
528 $segmentClass = new SegmentClass();
529 $segment = $segmentClass->getOneById($sendingParams['segment']['segment_id']);
530 if (!empty($segment)) {
531 $filters = $segment->filters;
532 }
533 }
534 }
535
536 return $filters;
537 }
538
539 public function send($campaignID, $result = 0, $abTestFinal = false)
540 {
541 $campaign = $this->getOneById($campaignID);
542
543 if (empty($campaign->mail_id)) {
544 $this->errors[] = acym_translation('ACYM_EMAIL_NOT_FOUND');
545
546 return false;
547 }
548
549 $filters = $this->getFilterCampaign($campaign->sending_params);
550 $pluginIsExisting = true;
551
552 if (empty($filters)) {
553 $filters = [0 => []];
554 acym_trigger('onAcymSendCampaignSpecial', [$campaign, &$filters[0], &$pluginIsExisting]);
555 } else {
556 foreach ($filters as $key => $filter) {
557 acym_trigger('onAcymSendCampaignSpecial', [$campaign, &$filters[$key], &$pluginIsExisting]);
558 }
559 }
560
561 if (!$pluginIsExisting) {
562 return false;
563 }
564
565 $lists = acym_loadResultArray('SELECT list_id FROM #__acym_mail_has_list WHERE mail_id = '.intval($campaign->mail_id));
566 if (empty($lists)) {
567 $this->errors[] = acym_translation('ACYM_NO_LIST_SELECTED');
568
569 return false;
570 }
571 acym_arrayToInteger($lists);
572
573 $date = acym_date('now', 'Y-m-d H:i:s', false);
574 $numberUsersInsertedByMailId = [];
575
576 if (empty($result)) {
577 $conditions = [
578 '`user`.`active` = 1',
579 '`ul`.`status` = 1',
580 '`ul`.`list_id` IN ('.implode(',', $lists).')',
581 ];
582 if ($this->config->get('require_confirmation', 1) == 1) {
583 $conditions[] = '`user`.`confirmed` = 1';
584 }
585
586 $automationHelper = new AutomationHelper();
587 $automationHelper->removeFlag(SegmentsController::FLAG_COUNT);
588 $automationHelper->join['ul'] = ' #__acym_user_has_list AS ul ON ul.user_id = user.id ';
589
590 if (!empty($filters)) {
591 $automationHelpers = [];
592 foreach ($filters as $or => $orValues) {
593 if (empty($orValues)) continue;
594
595 $automationHelpers[$or] = new AutomationHelper();
596 foreach ($orValues as $and => $andValues) {
597 $and = intval($and);
598 foreach ($andValues as $filterName => $options) {
599 acym_trigger('onAcymProcessFilter_'.$filterName, [&$automationHelpers[$or], &$options, &$and]);
600 }
601 }
602
603 $automationHelpers[$or]->addFlag(SegmentsController::FLAG_COUNT);
604 }
605
606 if (!empty($automationHelpers)) {
607 $excludingSegment = !empty($campaign->sending_params['segment']['invert']) && $campaign->sending_params['segment']['invert'] === 'exclude';
608 if ($excludingSegment) {
609 $conditions[] = 'user.automation NOT LIKE "%a'.SegmentsController::FLAG_COUNT.'a%"';
610 } else {
611 $conditions[] = 'user.automation LIKE "%a'.SegmentsController::FLAG_COUNT.'a%"';
612 }
613 }
614 }
615
616 if (acym_isMultilingual()) {
617 $select = ['IF(mail.id IS NULL, '.intval($campaign->mail_id).', `mail`.`id`)', 'ul.`user_id`', acym_escapeDB($date)];
618 $automationHelper->leftjoin['mail'] = '`#__acym_mail` AS mail ON `mail`.`language` = `user`.language AND `mail`.`parent_id` = '.intval($campaign->mail_id);
619 } else {
620 $select = [intval($campaign->mail_id), 'ul.`user_id`', acym_escapeDB($date)];
621 }
622
623 if (!empty($campaign->sending_params['resendTarget']) && 'new' === $campaign->sending_params['resendTarget']) {
624 if (acym_isMultilingual()) {
625 $automationHelper->leftjoin['us'] = '`#__acym_user_stat` AS `us` ON `us`.`user_id` = `user`.`id` AND `us`.`mail_id` IN (SELECT id FROM #__acym_mail WHERE parent_id = '.intval(
626 $campaign->mail_id
627 ).' OR id = '.intval($campaign->mail_id).')';
628 } else {
629 $automationHelper->leftjoin['us'] = '`#__acym_user_stat` AS `us` ON `us`.`user_id` = `user`.`id` AND `us`.`mail_id` = '.intval($campaign->mail_id);
630 }
631 $conditions[] = '`us`.`user_id` IS NULL';
632 }
633
634 $automationHelper->where = $conditions;
635 if (!empty($campaign->sending_params['abtest']['repartition']) && !$abTestFinal) {
636 $totalNumberOfUsers = acym_loadResult($automationHelper->getQuery(['count(user.id)']));
637 $numberOfUsersToSend = round($campaign->sending_params['abtest']['repartition'] * $totalNumberOfUsers / 100);
638
639 $numberOfUsersToSend1 = round($numberOfUsersToSend / 2);
640 $automationHelper->limit = '0, '.$numberOfUsersToSend1;
641 $select1 = [intval($campaign->mail_id), 'ul.`user_id`', acym_escapeDB($date)];
642 $numberUsersInsertedByMailId[intval($campaign->mail_id)] = acym_query(
643 'INSERT IGNORE INTO `#__acym_queue` (`mail_id`, `user_id`, `sending_date`) '.$automationHelper->getQuery($select1)
644 );
645
646 $numberOfUsersToSend2 = $numberOfUsersToSend1 * 2 > $numberOfUsersToSend ? $numberOfUsersToSend1 - 1 : $numberOfUsersToSend1;
647 $automationHelper->limit = $numberOfUsersToSend.', '.$numberOfUsersToSend2;
648 $select2 = [intval($campaign->sending_params['abtest']['B']), 'ul.`user_id`', acym_escapeDB($date)];
649 $numberUsersInsertedByMailId[intval($campaign->sending_params['abtest']['B'])] = acym_query(
650 'INSERT IGNORE INTO `#__acym_queue` (`mail_id`, `user_id`, `sending_date`) '.$automationHelper->getQuery($select2)
651 );
652 } elseif ($abTestFinal && !empty($campaign->sending_params['abtest']['final'])) {
653 $mailIds = [$campaign->mail_id, $campaign->sending_params['abtest']['B']];
654 acym_arrayToInteger($mailIds);
655 $automationHelper->where[] = 'user.id NOT IN (SELECT user_id FROM #__acym_user_stat WHERE mail_id IN ('.implode(',', $mailIds).'))';
656
657 $select = [intval($campaign->sending_params['abtest']['final']), 'ul.`user_id`', acym_escapeDB($date)];
658 $numberUsersInsertedByMailId[intval($campaign->sending_params['abtest']['final'])] = acym_query(
659 'INSERT IGNORE INTO `#__acym_queue` (`mail_id`, `user_id`, `sending_date`) '.$automationHelper->getQuery($select)
660 );
661 } else {
662 $insertQuery = 'INSERT IGNORE INTO `#__acym_queue` (`mail_id`, `user_id`, `sending_date`) '.$automationHelper->getQuery($select);
663 $numberUsersInsertedByMailId[intval($campaign->mail_id)] = acym_query($insertQuery);
664 }
665
666 $automationHelper->removeFlag(SegmentsController::FLAG_COUNT);
667 } else {
668 $numberUsersInsertedByMailId[intval($campaign->mail_id)] = $result;
669 }
670
671
672 $result += array_sum($numberUsersInsertedByMailId);
673
674 if ($campaign->sending_type == self::SENDING_TYPE_NOW) {
675 $campaign->sending_date = $date;
676 $campaign->draft = 0;
677 $this->save($campaign);
678 }
679
680 $mailStatClass = new MailStatClass();
681 foreach ($numberUsersInsertedByMailId as $mailId => $numberOfUsersInserted) {
682 $newMailStat = [
683 'mail_id' => intval($mailId),
684 'total_subscribers' => intval($numberOfUsersInserted),
685 'send_date' => $date,
686 ];
687
688 $mailStatClass->save($newMailStat);
689 }
690
691 if ($result === 0) {
692 $this->errors[] = acym_translation('ACYM_NO_SUBSCRIBERS_FOUND');
693
694 return false;
695 }
696
697 acym_query('UPDATE `#__acym_campaign` SET `sent` = 1, `active` = 1 WHERE `mail_id` = '.intval($campaign->mail_id));
698
699 return $result;
700 }
701
702 public function getCampaignForDashboard()
703 {
704 $query = 'SELECT campaign.*, mail.name as name FROM #__acym_campaign as campaign LEFT JOIN #__acym_mail as mail ON campaign.mail_id = mail.id WHERE `active` = 1 AND `sending_type` = '.acym_escapeDB(
705 self::SENDING_TYPE_SCHEDULED
706 ).' AND `sent` = 0 LIMIT 3';
707
708 return $this->decode(acym_loadObjectList($query));
709 }
710
711 public function getOpenRateCampaigns($mailIds)
712 {
713 if (!is_array($mailIds)) $mailIds = [$mailIds];
714 acym_arrayToInteger($mailIds);
715
716 $query = 'SELECT SUM(sent) AS sent, SUM(open_unique) AS open_unique FROM #__acym_mail_stat
717 WHERE mail_id IN ('.implode(',', $mailIds).')';
718
719 return acym_loadObject($query);
720 }
721
722 public function getOpenRateAllCampaign()
723 {
724 $query = 'SELECT SUM(sent) as sent, SUM(open_unique) as open_unique FROM #__acym_mail_stat';
725
726 return acym_loadObject($query);
727 }
728
729 public function getBounceRateAllCampaign()
730 {
731 $query = 'SELECT SUM(sent) as sent, SUM(bounce_unique) as bounce_unique FROM #__acym_mail_stat';
732
733 return acym_loadObject($query);
734 }
735
736
737 public function getBounceRateCampaigns($mailIds)
738 {
739 if (!is_array($mailIds)) $mailIds = [$mailIds];
740
741 acym_arrayToInteger($mailIds);
742
743 $query = 'SELECT SUM(sent) AS sent, SUM(bounce_unique) AS bounce_unique FROM #__acym_mail_stat
744 WHERE mail_id IN ('.implode(',', $mailIds).')';
745
746 return acym_loadObject($query);
747 }
748
749 public function getOpenByMonth($mailIds = [], $start = '', $end = '')
750 {
751 if (!is_array($mailIds)) $mailIds = [$mailIds];
752 acym_arrayToInteger($mailIds);
753
754 $query = 'SELECT COUNT(user_id) as open, DATE_FORMAT(open_date, \'%Y-%m\') as open_date FROM #__acym_user_stat WHERE open > 0';
755 $query .= empty($mailIds) ? '' : ' AND `mail_id` IN ('.implode(',', $mailIds).')';
756 $query .= ' AND `open_date` > "0000-00-00"';
757 $query .= empty($start) ? '' : ' AND `open_date` >= '.acym_escapeDB($start);
758 $query .= empty($end) ? '' : ' AND `open_date` <= '.acym_escapeDB($end);
759 $query .= ' GROUP BY MONTH(open_date), YEAR(open_date) ORDER BY open_date';
760
761 return acym_loadObjectList($query);
762 }
763
764 public function getOpenByWeek($mail_id = '', $start = '', $end = '')
765 {
766 $query = 'SELECT COUNT(user_id) as open, DATE_FORMAT(open_date, \'%Y-%m-%d\') as open_date FROM #__acym_user_stat WHERE open > 0';
767 $query .= empty($mail_id) ? '' : ' AND `mail_id`='.intval($mail_id);
768 $query .= ' AND `open_date` > "0000-00-00"';
769 $query .= empty($start) ? '' : ' AND `open_date` >= '.acym_escapeDB($start);
770 $query .= empty($end) ? '' : ' AND `open_date` <= '.acym_escapeDB($end);
771 $query .= ' GROUP BY WEEK(open_date), YEAR(open_date) ORDER BY open_date';
772
773 return acym_loadObjectList($query);
774 }
775
776 public function getOpenByDay($mailIds = [], $start = '', $end = '')
777 {
778 if (!is_array($mailIds)) $mailIds = [$mailIds];
779 acym_arrayToInteger($mailIds);
780
781 $query = 'SELECT COUNT(user_id) as open, DATE_FORMAT(open_date, \'%Y-%m-%d\') as open_date FROM #__acym_user_stat WHERE open > 0';
782 $query .= empty($mailIds) ? '' : ' AND `mail_id` IN ('.implode(',', $mailIds).')';
783 $query .= ' AND `open_date` > "0000-00-00"';
784 $query .= empty($start) ? '' : ' AND `open_date` >= '.acym_escapeDB($start);
785 $query .= empty($end) ? '' : ' AND `open_date` <= '.acym_escapeDB($end);
786 $query .= ' GROUP BY DAYOFYEAR(open_date), YEAR(open_date) ORDER BY open_date';
787
788 return acym_loadObjectList($query);
789 }
790
791 public function getOpenByHour($mailIds = [], $start = '', $end = '')
792 {
793 if (!is_array($mailIds)) $mailIds = [$mailIds];
794 acym_arrayToInteger($mailIds);
795
796 $query = 'SELECT COUNT(user_id) as open, DATE_FORMAT(open_date, \'%Y-%m-%d %H:00:00\') as open_date FROM #__acym_user_stat WHERE open > 0';
797 $query .= empty($mailIds) ? '' : ' AND `mail_id` IN ('.implode(',', $mailIds).')';
798 $query .= ' AND `open_date` > "0000-00-00 00:00:00"';
799 $query .= empty($start) ? '' : ' AND `open_date` >= '.acym_escapeDB($start);
800 $query .= empty($end) ? '' : ' AND `open_date` <= '.acym_escapeDB($end);
801 $query .= ' GROUP BY HOUR(open_date), DAYOFYEAR(open_date), YEAR(open_date) ORDER BY open_date';
802
803 return acym_loadObjectList($query);
804 }
805
806 public function getDevicesWithCountByMailId($mailIds = [])
807 {
808 if (!is_array($mailIds)) $mailIds = [$mailIds];
809 acym_arrayToInteger($mailIds);
810
811 $query = 'SELECT device, COUNT(*) as number FROM #__acym_user_stat WHERE `open` > 0';
812 if (!empty($mailIds)) $query .= ' AND mail_id IN ('.implode(',', $mailIds).')';
813 $query .= ' GROUP BY device';
814
815 return acym_loadObjectList($query);
816 }
817
818 public function getLastNewsletters(&$params): array
819 {
820 $querySelect = 'SELECT mail.*, campaign.sending_date ';
821 $queryCountSelect = 'SELECT COUNT(*) FROM (SELECT DISTINCT mail.id ';
822
823 $query = 'FROM #__acym_campaign AS campaign
824 JOIN #__acym_mail AS mail ON campaign.mail_id = mail.id ';
825
826 if (isset($params['userId']) || isset($params['lists'])) {
827 $query .= 'JOIN #__acym_mail_has_list AS maillist ON mail.id = maillist.mail_id ';
828 if (isset($params['userId'])) $query .= 'JOIN #__acym_user_has_list AS userlist ON maillist.list_id = userlist.list_id ';
829 }
830
831 $where = 'WHERE campaign.active = 1 AND campaign.sent = 1 AND mail.type = '.acym_escapeDB(MailClass::TYPE_STANDARD).' AND campaign.visible = 1 ';
832
833 if (isset($params['lists'])) {
834 acym_arrayToInteger($params['lists']);
835 $where .= 'AND maillist.list_id IN ('.implode(', ', $params['lists']).') ';
836 }
837
838 if (isset($params['userId']) && !empty($params['displayUserListOnly'])) {
839 $where .= 'AND userlist.user_id = '.intval($params['userId']).' ';
840 }
841
842 if (isset($params['search'])) {
843 $search = acym_escapeDB('%'.acym_utf8Encode($params['search']).'%');
844 $where .= 'AND (mail.subject LIKE '.$search.' OR mail.body LIKE '.$search.')';
845 }
846
847 $query .= $where;
848 $return = [];
849 $return['count'] = acym_loadResult($queryCountSelect.$query.') AS r ');
850
851 $endQuerySelect = 'GROUP BY mail.id ';
852 $endQuerySelect .= 'ORDER BY campaign.sending_date DESC';
853
854 $page = $params['page'] ?? 0;
855 $numberPerPage = $params['numberPerPage'] ?? 0;
856 $lastNewsletters = $params['limit'] ?? 0;
857
858 if (!empty($page) && !empty($numberPerPage)) {
859 if (!empty($lastNewsletters)) {
860 $limit = $page * $numberPerPage > $lastNewsletters ? fmod($lastNewsletters, $numberPerPage) : $numberPerPage;
861 } else {
862 $limit = $numberPerPage;
863 $nbTotalPage = ceil($return['count'] / $numberPerPage);
864 if ($params['page'] > $nbTotalPage) {
865 $params['page'] = 1;
866 }
867 }
868
869 $offset = ($params['page'] - 1) * $numberPerPage;
870 $endQuerySelect .= ' LIMIT '.intval($offset).', '.intval($limit);
871 } elseif (!empty($lastNewsletters)) {
872 $limit = $lastNewsletters;
873
874 $endQuerySelect .= ' LIMIT '.intval($limit);
875 }
876
877
878 $return['matchingNewsletters'] = $this->decode(acym_loadObjectList($querySelect.$query.$endQuerySelect));
879
880 if (isset($params['language']) && !empty($return['matchingNewsletters'])) {
881 $mailIds = array_column($return['matchingNewsletters'], 'id');
882
883 $translatedEmails = $this->decode(
884 acym_loadObjectList(
885 'SELECT mail.*
886 FROM #__acym_mail AS mail
887 WHERE mail.parent_id IN ('.implode(',', $mailIds).') AND mail.language = '.acym_escapeDB($params['language']),
888 'parent_id'
889 )
890 );
891 }
892
893 $userClass = new UserClass();
894 $userEmail = acym_currentUserEmail();
895 $user = $userClass->getOneByEmail($userEmail);
896
897 foreach ($return['matchingNewsletters'] as $i => $oneNewsletter) {
898 if (isset($translatedEmails[$oneNewsletter->id])) {
899 $translatedEmails[$oneNewsletter->id]->sending_date = $oneNewsletter->sending_date;
900 $oneNewsletter = $translatedEmails[$oneNewsletter->id];
901 }
902 acym_trigger('replaceContent', [&$oneNewsletter, false]);
903 acym_trigger('replaceUserInformation', [&$oneNewsletter, &$user, false]);
904
905 $return['matchingNewsletters'][$i] = $oneNewsletter;
906 }
907
908 return $return;
909 }
910
911 public function getListsByMailId($mailId)
912 {
913 $query = 'SELECT list_id FROM #__acym_mail_has_list WHERE mail_id = '.intval($mailId);
914
915 return acym_loadResultArray($query);
916 }
917
918 public function triggerAutoCampaign()
919 {
920 $activeAutoCampaigns = acym_loadObjectList(
921 'SELECT campaign.*, mail.name
922 FROM #__acym_campaign AS campaign
923 JOIN #__acym_mail AS mail ON campaign.`mail_id` = mail.`id`
924 WHERE `active` = 1 AND `sending_type` = '.acym_escapeDB(self::SENDING_TYPE_AUTO)
925 );
926 $activeAutoCampaigns = $this->decode($activeAutoCampaigns);
927
928 if (empty($activeAutoCampaigns)) return;
929
930 $mailClass = new MailClass();
931 $time = time();
932
933 foreach ($activeAutoCampaigns as $campaign) {
934 if (!empty($campaign->sending_params['start_date']) && (int)acym_getTime(acym_date($campaign->sending_params['start_date'], 'Y-m-d H:i')) > $time) continue;
935
936 $step = new \stdClass();
937 $step->triggers = $campaign->sending_params;
938 $step->last_execution = $campaign->last_generated;
939 $step->next_execution = $campaign->next_trigger;
940
941 $data = ['time' => $time];
942 $execute = !empty($step->next_execution) && $step->next_execution <= $data['time'];
943 acym_trigger('onAcymExecuteTrigger', [&$step, &$execute, &$data], 'plgAcymTime');
944 $campaign->next_trigger = $step->next_execution;
945 if (!$execute) {
946 $this->save($campaign);
947 continue;
948 }
949
950 $campaignMail = $mailClass->getOneById($campaign->mail_id);
951
952 $lastGenerated = $campaign->last_generated;
953 $shouldGenerate = $this->updateAutoCampaign($campaign, $campaignMail, $time);
954 $this->save($campaign);
955
956 if (!$shouldGenerate) continue;
957
958 $generatedCampaign = $this->generateCampaign($campaign, $campaignMail, $lastGenerated, $mailClass);
959
960 if (empty($campaign->sending_params['need_confirm_to_send'])) {
961 $this->send($generatedCampaign->id);
962 }
963 }
964 }
965
966 private function shouldGenerateCampaign($campaign, $campaignMail): bool
967 {
968 $results = acym_trigger('generateByCategory', [&$campaignMail], null, function ($plugin) {
969 $plugin->generateCampaignResult->status = true;
970 });
971
972 foreach ($results as $oneResult) {
973 if (isset($oneResult->status) && !$oneResult->status) {
974 $this->messages[] = acym_translationSprintf('ACYM_CAMPAIGN_NOT_GENERATED', $campaign->name, $oneResult->message);
975
976 return false;
977 }
978 }
979
980 return true;
981 }
982
983 private function updateAutoCampaign(&$campaign, $campaignMail, $time): bool
984 {
985 if (!$this->shouldGenerateCampaign($campaign, $campaignMail)) {
986 return false;
987 }
988
989 if (empty($campaign->sending_params['number_generated'])) {
990 $campaign->sending_params['number_generated'] = 1;
991 } else {
992 $campaign->sending_params['number_generated']++;
993 }
994 $campaign->last_generated = $time;
995
996 $this->messages[] = acym_translationSprintf('ACYM_CAMPAIGN_GENERATED', $campaign->name, $campaign->sending_params['number_generated']);
997
998 return true;
999 }
1000
1001 private function generateCampaign($campaign, $campaignMail, $lastGenerated, $mailClass)
1002 {
1003 $newMail = $this->generateMailAutoCampaign($campaignMail, $campaign->sending_params['number_generated'], $mailClass);
1004 $newCampaign = new \stdClass();
1005 $newCampaign->mail_id = $newMail->id;
1006 $newCampaign->parent_id = $campaign->id;
1007 $newCampaign->active = 1;
1008 $newCampaign->visible = $campaign->visible;
1009 $newCampaign->draft = 1;
1010 $newCampaign->sending_type = self::SENDING_TYPE_NOW;
1011 $newCampaign->sent = 0;
1012 $newCampaign->last_generated = $lastGenerated;
1013 $newCampaign->sending_params = empty($campaign->sending_params['segment']) ? '' : ['segment' => $campaign->sending_params['segment']];
1014
1015 $newCampaign->id = $this->save($newCampaign);
1016
1017 acym_trigger('replaceContent', [&$newMail, false]);
1018 $mailClass->save($newMail);
1019
1020 return $newCampaign;
1021 }
1022
1023 private function generateMailAutoCampaign($newMail, $generatedMailNumber, $mailClass)
1024 {
1025 $mailId = $newMail->id;
1026 unset($newMail->id);
1027 $newMail->creation_date = acym_date('now', 'Y-m-d H:i:s', false);
1028 $newMail->name .= ' #'.$generatedMailNumber;
1029
1030 $newMail->id = $mailClass->save($newMail);
1031 $this->setListToGeneratedCampaign($mailId, $newMail->id, $mailClass);
1032
1033 if (acym_isMultilingual()) {
1034 $this->generateMailAutoCampaignMultilingual($mailId, $generatedMailNumber, $newMail->id);
1035 }
1036
1037 return $newMail;
1038 }
1039
1040 private function generateMailAutoCampaignMultilingual($mailId, $generatedMailNumber, $newParentId)
1041 {
1042 $mailClass = new MailClass();
1043 $mails = $mailClass->getTranslationsById($mailId, true);
1044
1045 foreach ($mails as $mail) {
1046 unset($mail->id);
1047 $mail->name .= ' #'.$generatedMailNumber;
1048 $mail->parent_id = $newParentId;
1049
1050 $mailClass->save($mail);
1051 }
1052 }
1053
1054 private function setListToGeneratedCampaign($parentMailId, $newMailId, $mailClass)
1055 {
1056 $lists = $mailClass->getAllListsByMailId($parentMailId);
1057 $listIds = array_keys($lists);
1058
1059 $this->manageListsToCampaign($listIds, $newMailId);
1060 }
1061
1062 public function getLastGenerated($mailId)
1063 {
1064 return acym_loadResult(
1065 'SELECT `last_generated`
1066 FROM #__acym_campaign
1067 WHERE `mail_id` = '.intval($mailId)
1068 );
1069 }
1070
1071 public function getAllCampaignsGenerated()
1072 {
1073 $query = 'SELECT id FROM #__acym_campaign WHERE parent_id IS NOT NULL AND sending_type = '.acym_escapeDB(self::SENDING_TYPE_NOW);
1074
1075 return acym_loadObjectList($query);
1076 }
1077
1078 public function getAllCampaignsGeneratedWaiting()
1079 {
1080 return acym_loadObjectList(
1081 'SELECT id
1082 FROM #__acym_campaign
1083 WHERE parent_id IS NOT NULL
1084 AND sending_type = '.acym_escapeDB(self::SENDING_TYPE_NOW).'
1085 AND draft = 1
1086 AND active = 1
1087 AND sent = 0'
1088 );
1089 }
1090
1091 public function getCampaignsByTypes($campaignTypes, $onlyActives = false)
1092 {
1093 if (empty($campaignTypes)) return [];
1094 $campaignTypes = array_map('acym_escapeDB', $campaignTypes);
1095 $query = 'SELECT campaign.* FROM #__acym_campaign AS campaign WHERE campaign.sending_type IN ('.implode(',', $campaignTypes).')';
1096 if ($onlyActives) $query .= ' AND campaign.active = 1';
1097
1098 return $this->decode(acym_loadObjectList($query), false);
1099 }
1100
1101 public function getCountCampaignType($type)
1102 {
1103 if (empty($type)) return 0;
1104
1105 return acym_loadResult('SELECT COUNT(*) FROM #__acym_campaign WHERE sending_type = '.acym_escapeDB($type));
1106 }
1107
1108 public function getAllAbTestCampaignsToFinishSending()
1109 {
1110 $query = 'SELECT * FROM #__acym_campaign WHERE sending_params LIKE \'%"abtest":%\' AND draft = 0 AND active = 1 AND sent = 1';
1111
1112 return $this->decode(acym_loadObjectList($query));
1113 }
1114
1115 public function finishAbTestCampaign($campaign)
1116 {
1117 $abTestParams = empty($campaign->sending_params['abtest']) ? [] : $campaign->sending_params['abtest'];
1118
1119 if (empty($abTestParams['B']) || empty($abTestParams['repartition']) || !empty($abTestParams['abtest_finished']) || empty($abTestParams['after']['action'])) {
1120 return false;
1121 }
1122
1123 if (strtotime($campaign->sending_date.' + '.$abTestParams['after']['days'].' days') > time()) {
1124 return false;
1125 }
1126
1127 $mailStatsClass = new MailStatClass();
1128 $mailIds = [$campaign->mail_id, $abTestParams['B']];
1129 $bestMails = $mailStatsClass->getBestEmailByStats($mailIds, $abTestParams['after']['action']);
1130
1131 if (empty($bestMails)) {
1132 return false;
1133 }
1134
1135 $mailClass = new MailClass();
1136
1137 switch ($abTestParams['after']['action']) {
1138 case 'open_rate':
1139 case 'click_rate':
1140 $mail = $mailClass->getOneById($bestMails[$abTestParams['after']['action']]);
1141
1142 if (empty($mail)) {
1143 return false;
1144 }
1145 break;
1146 case 'click_open_rate':
1147 $bestMailClickRate = $mailClass->getOneById($bestMails['click_rate']);
1148 $bestMailOpenRate = $mailClass->getOneById($bestMails['open_rate']);
1149
1150 if (empty($bestMailClickRate) || empty($bestMailOpenRate)) {
1151 return false;
1152 }
1153
1154 $mail = $bestMailClickRate;
1155 $mail->preheader = $bestMailOpenRate->preheader;
1156 $mail->subject = $bestMailOpenRate->subject;
1157 $mail->from_name = $bestMailOpenRate->from_name;
1158 $mail->from_email = $bestMailOpenRate->from_email;
1159 $mail->reply_to_name = $bestMailOpenRate->reply_to_name;
1160 $mail->reply_to_email = $bestMailOpenRate->reply_to_email;
1161 break;
1162 }
1163
1164 $mail->id = 0;
1165 $mail->parent_id = $campaign->mail_id;
1166 $mail->name .= ' - '.acym_translation('ACYM_ABTEST_WINNER');
1167 $newMailId = $mailClass->save($mail);
1168 if (!$newMailId) {
1169 return false;
1170 }
1171
1172 $campaign->sending_params['abtest']['abtest_finished'] = 1;
1173 $campaign->sending_params['abtest']['final'] = $newMailId;
1174
1175 $this->save($campaign);
1176 $this->send($campaign->id, 0, true);
1177
1178 return true;
1179 }
1180
1181 public function isAbTestMail($mailId)
1182 {
1183 $query = 'SELECT * FROM #__acym_campaign WHERE mail_id = '.intval($mailId).' AND sending_params LIKE \'%"abtest":%\'';
1184
1185 if (!empty(acym_loadObject($query))) {
1186 return true;
1187 }
1188
1189 $query = 'SELECT * FROM #__acym_campaign AS campaign JOIN #__acym_mail AS mail ON campaign.mail_id = mail.parent_id AND mail.id = '.intval(
1190 $mailId
1191 ).' WHERE sending_params LIKE \'%"abtest":%\'';
1192
1193 return !empty(acym_loadObject($query));
1194 }
1195
1196 public function resetAbTestVersion($campaignId): void
1197 {
1198 if (empty($campaignId)) {
1199 return;
1200 }
1201
1202 $campaign = $this->getOneById($campaignId);
1203
1204 if (empty($campaign)) {
1205 return;
1206 }
1207
1208 if (empty($campaign->sending_params['abtest'])) {
1209 return;
1210 }
1211
1212 foreach (self::AB_TEST_VERSIONS as $version) {
1213 if (!empty($campaign->sending_params['abtest'][$version])) {
1214 $mailClass = new MailClass();
1215 $mailClass->delete($campaign->sending_params['abtest'][$version]);
1216 unset($campaign->sending_params['abtest'][$version]);
1217 }
1218 }
1219
1220 $this->save($campaign);
1221 }
1222
1223 public function getXCampaigns(array $options)
1224 {
1225 $limit = $options['limit'] ?? 10;
1226 $offset = $options['offset'] ?? 0;
1227 $filters = $options['filters'] ?? [];
1228
1229 $conditions = [];
1230 foreach ($filters as $column => $filter) {
1231 switch ($column) {
1232 case 'id':
1233 case 'draf':
1234 case 'active':
1235 case 'mail_id':
1236 case 'sent':
1237 case 'parent_id':
1238 case 'next_trigger':
1239 case 'visible':
1240 case 'last_trigger':
1241 $conditions[] = 'campaign.'.acym_secureDBColumn($column).' = '.intval($filter);
1242 break;
1243 case 'name':
1244 case 'subject':
1245 $conditions[] = 'mail.'.acym_secureDBColumn($column).' LIKE '.acym_escapeDB('%'.$filter.'%');
1246 break;
1247 default:
1248 $conditions[] = 'campaign.'.acym_secureDBColumn($column).' = '.acym_escapeDB('%'.$filter.'%');
1249 }
1250 }
1251
1252 $query = 'SELECT campaign.*, mail.subject, mail.name FROM #__acym_campaign AS campaign
1253 LEFT JOIN #__acym_mail AS mail ON campaign.mail_id = mail.id';
1254
1255 if (!empty($conditions)) {
1256 $query .= ' WHERE '.implode(' AND ', $conditions);
1257 }
1258
1259 return $this->decode(acym_loadObjectList($query, $this->pkey, $offset, $limit));
1260 }
1261 }
1262