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

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