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

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