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 / Controllers / Stats / GlobalStats.php

GlobalStats.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 10.2.2, at back/Controllers/Stats/GlobalStats.php

883 lines 33.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Controllers\Stats;
4
5 use AcyMailing\Classes\CampaignClass;
6 use AcyMailing\Classes\MailClass;
7 use AcyMailing\Classes\MailStatClass;
8 use AcyMailing\Classes\UrlClickClass;
9 use AcyMailing\Classes\UserStatClass;
10 use AcyMailing\Helpers\ExportHelper;
11 use AcyMailing\Helpers\MailerHelper;
12 use AcyMailing\Helpers\PaginationHelper;
13 use AcyMailing\Helpers\WorkflowHelper;
14 use AcyMailing\Libraries\Browser\BrowserDetection;
15 use AcyMailing\Classes\UserClass;
16
17 trait GlobalStats
18 {
19 public function globalStats(): void
20 {
21 acym_setVar('layout', 'global_stats');
22
23 $data = [];
24
25 $this->prepareDefaultPageInfo($data);
26
27 $this->prepareOpenTimeChart($data);
28 $this->preparecharts($data);
29 $this->prepareListReceivers($data);
30 $this->prepareDefaultRoundCharts($data);
31 $this->prepareDefaultLineChart($data);
32 $this->prepareDefaultDevicesChart($data);
33 $this->prepareDefaultBrowsersChart($data);
34 if (count($this->selectedMailIds) == 1) {
35 if ($data['isAbTest']) {
36 $this->prepareAbTestMails($data);
37 } elseif (acym_isMultilingual()) {
38 $this->prepareMultilingualMails($data);
39 }
40 }
41 $this->prepareMailFilter($data);
42
43 parent::display($data);
44 }
45
46 private function prepareMailFilter(array &$data): void
47 {
48 $data['input_mail_ids'] = '';
49
50 if (!empty($this->multiLanguageMailAdded) || !empty($this->generatedMailAdded)) {
51 $this->selectedMailIds = array_filter(
52 $this->selectedMailIds,
53 function ($mailId) {
54 return !in_array($mailId, $this->multiLanguageMailAdded) && !in_array($mailId, $this->generatedMailAdded);
55 }
56 );
57 }
58
59 if (count($this->selectedMailIds) > 1) {
60 $data['input_mail_ids'] = '<input type="hidden" value="'.implode(',', $this->selectedMailIds).'" name="mail_ids">';
61 }
62
63 $attributes = [
64 'class' => 'acym__select acym_select2_ajax acym__stats__select__mails',
65 'data-placeholder' => acym_translation('ACYM_ALL_EMAILS'),
66 'data-ctrl' => 'stats',
67 'data-task' => 'searchSentMail',
68 'data-min' => '0',
69 'id' => 'mail_ids',
70 ];
71
72 if (!empty($this->selectedMailIds)) {
73 $attributes['data-selected'] = implode(',', $this->selectedMailIds);
74 }
75
76 $data['mail_filter'] = acym_selectMultiple(
77 [],
78 'mail_ids',
79 [],
80 $attributes
81 );
82
83 $data['emailVersionsFilters'] = '';
84
85 if (!empty($data['emailVersions'])) {
86 $data['emailVersionsFilters'] = acym_select(
87 $data['emailVersions'],
88 'mail_id_version',
89 $this->selectedMailIds[0],
90 [
91 'class' => 'acym__select acym__stats__select__language',
92 ]
93 );
94 }
95 if (!empty($data['emailTranslations'])) {
96 $data['emailVersionsFilters'] = acym_select(
97 $data['emailTranslations'],
98 'mail_id_version',
99 $this->selectedMailIds[0],
100 [
101 'class' => 'acym__select acym__stats__select__language',
102 ]
103 );
104 }
105 }
106
107 private function prepareClickStats(array &$data): void
108 {
109 if (empty($data['selectedMailid'])) {
110 return;
111 }
112
113 $urlClickClass = new UrlClickClass();
114 $allClickInfo = $urlClickClass->getAllLinkFromEmail($this->selectedMailIds[0]);
115
116 $data['url_click'] = [];
117 $data['url_click']['allClick'] = $allClickInfo['allClick'];
118
119 $allPercentage = [];
120 foreach ($allClickInfo['urls_click'] as $url) {
121 $percentage = 0;
122 if (empty($url->click)) {
123 $data['url_click'][$url->name] = ['percentage' => $percentage, 'numberClick' => '0'];
124 } else {
125 $percentage = intval(($url->click * 100) / $allClickInfo['allClick']);
126 $data['url_click'][$url->name] = ['percentage' => $percentage, 'numberClick' => $url->click];
127 }
128 $allPercentage[] = $percentage;
129 }
130
131 $helperMailer = new MailerHelper();
132 if (!empty($data['mailInformation'])) {
133 $helperMailer->body = $data['mailInformation']->body;
134 $helperMailer->statClick(intval($data['mailInformation']->id), 0, true);
135 $data['mailInformation']->body = preg_replace('#&(amp;)?autoSubId=[^"]+"#Uis', '"', $helperMailer->body);
136 }
137
138 if (!empty($allPercentage)) {
139 $maxPercentage = max($allPercentage);
140
141 foreach ($data['url_click'] as $name => $val) {
142 if ($name === 'allClick') continue;
143 $percentageRecalc = intval(($val['percentage'] * 100) / $maxPercentage);
144 if ($percentageRecalc <= 33) {
145 $data['url_click'][$name]['color'] = '0, 164, 255';
146 } elseif ($percentageRecalc <= 66) {
147 $data['url_click'][$name]['color'] = '248, 31, 255';
148 } else {
149 $data['url_click'][$name]['color'] = '255, 82, 89';
150 }
151 }
152 }
153
154 $data['url_click'] = json_encode($data['url_click']);
155
156 $data['url_foundation_email'] = ACYM_CSS.'libraries/foundation_email.min.css?v='.filemtime(ACYM_MEDIA.'css'.DS.'libraries'.DS.'foundation_email.min.css');
157 $data['url_click_map_email'] = ACYM_CSS.'click_map.min.css?v='.filemtime(ACYM_MEDIA.'css'.DS.'click_map.min.css');
158 }
159
160 public function prepareDefaultRoundCharts(array &$data): void
161 {
162 $charts = [
163 'delivery' => [
164 'percentage' => 95,
165 'text' => 'ACYM_SUCCESSFULLY_SENT',
166 ],
167 'open' => [
168 'percentage' => 25,
169 'text' => 'ACYM_OPEN_RATE',
170 ],
171 'click' => [
172 'percentage' => 10,
173 'text' => 'ACYM_CLICK_RATE',
174 ],
175 'fail' => [
176 'percentage' => 2,
177 'text' => 'ACYM_BOUNCE_RATE',
178 ],
179 'unsub' => [
180 'percentage' => 3,
181 'text' => 'ACYM_UNSUBSCRIBE',
182 ],
183 ];
184
185 $data['example_round_chart'] = '';
186 foreach ($charts as $type => $oneChart) {
187 if ($type == 'unsub' && empty($this->selectedMailIds)) continue;
188 $data['example_round_chart'] .= '<div class="cell acym__stats__donut__one-chart">';
189 $data['example_round_chart'] .= acym_roundChart(
190 $oneChart['percentage'],
191 $type,
192 '',
193 acym_translation($oneChart['text'])
194 );
195 $data['example_round_chart'] .= '</div>';
196 }
197 }
198
199 public function prepareDefaultLineChart(array &$data): void
200 {
201 $dataMonth = [];
202 $dataMonth['Jan 18'] = ['open' => '150', 'click' => '40'];
203 $dataDay = [];
204 $dataDay['23 Jan'] = ['open' => '150', 'click' => '40'];
205 $dataHour = [];
206 $dataHour['23 Jan 08:00'] = ['open' => '25', 'click' => '10'];
207 $dataHour['23 Jan 09:00'] = ['open' => '50', 'click' => '10'];
208 $dataHour['23 Jan 10:00'] = ['open' => '16', 'click' => '10'];
209 $dataHour['23 Jan 11:00'] = ['open' => '59', 'click' => '10'];
210 $data['example_line_chart'] = acym_lineChart($dataMonth, $dataDay, $dataHour);
211 }
212
213 public function prepareDefaultDevicesChart(array &$data): void
214 {
215 $defaultData = [
216 'ACYM_MOBILE' => 25662,
217 'ACYM_DESKTOP' => 12471,
218 'ACYM_OTHER' => 3548,
219 'ACYM_UNKNOWN' => 6213,
220 ];
221
222 $data['example_devices_chart'] = acym_pieChart($defaultData, '', acym_translation('ACYM_DEVICES'));
223 }
224
225 public function prepareDefaultBrowsersChart(array &$data): void
226 {
227 $exampleData = [
228 'Apple Mail' => 15835,
229 'Google Chrome' => 13375,
230 'Safari' => 2667,
231 'Outlook' => 2458,
232 'Edge' => 1190,
233 'Firefox' => 826,
234 'ACYM_OTHER' => 4775,
235 'ACYM_UNKNOWN' => 6123,
236 ];
237
238 $data['example_source_chart'] = acym_pieChart($exampleData, '', acym_translation('ACYM_OPENED_WITH'));
239 }
240
241 public function setDataForChartLine(): void
242 {
243 $newStart = acym_date(acym_getVar('string', 'start'), 'Y-m-d H:i:s');
244 $newEnd = acym_date(acym_getVar('string', 'end'), 'Y-m-d H:i:s');
245 $mailIds = acym_getVar('int', 'id');
246
247 if (empty($mailIds)) {
248 $mailIds = [];
249 }
250
251 $mailClass = new MailClass();
252 if (!empty($mailIds)) {
253 $mailIds = $mailClass->getAutomaticMailIds($mailIds);
254 }
255
256 if (!empty($mailIds) && !is_array($mailIds)) {
257 $mailIds = [$mailIds];
258 }
259
260 if ($newStart >= $newEnd) {
261 echo 'error';
262 exit;
263 }
264
265 $statsCampaignSelected = new \stdClass();
266 $this->prepareLineChart($statsCampaignSelected, $mailIds, $newStart, $newEnd);
267
268 echo acym_lineChart($statsCampaignSelected->month, $statsCampaignSelected->day, $statsCampaignSelected->hour, true);
269 exit;
270 }
271
272 private function getValues(string $modifier, string $intervalCode, array $campaignOpens, array $campaignClicks, string $dateCode, bool $hour = false): array
273 {
274 $opens = [];
275 foreach ($campaignOpens as $one) {
276 $opens[acym_date(acym_getTime($one->open_date), $dateCode)] = $one->open;
277 }
278
279 $clicks = [];
280 foreach ($campaignClicks as $one) {
281 $clicks[acym_date(acym_getTime($one->date_click), $dateCode)] = $one->click;
282 }
283
284 $begin = new \DateTime(empty($campaignClicks) ? $campaignOpens[0]->open_date : min([$campaignOpens[0]->open_date, $campaignClicks[0]->date_click]));
285 $end = new \DateTime(empty($campaignClicks) ? end($campaignOpens)->open_date : max([end($campaignOpens)->open_date, end($campaignClicks)->date_click]));
286
287 $end->modify('+1 '.$modifier);
288
289 $interval = new \DateInterval($intervalCode);
290 $daterange = new \DatePeriod($begin, $interval, $end);
291
292 $result = [];
293 foreach ($daterange as $date) {
294 $one = $date->format($dateCode);
295
296 $current = [];
297 $current['open'] = empty($opens[$one]) ? 0 : $opens[$one];
298 $current['click'] = empty($clicks[$one]) ? 0 : $clicks[$one];
299
300 $key = $hour ? $one.':00' : $one;
301 $result[$key] = $current;
302 }
303
304 return $result;
305 }
306
307 public function prepareLineChart(object &$statsCampaignSelected, array $mailIdsOfCampaign, string $newStart = '', string $newEnd = ''): void
308 {
309 $campaignClass = new CampaignClass();
310 $statsCampaignSelected->hasStats = true;
311
312 $campaignOpenByMonth = $campaignClass->getOpenByMonth($mailIdsOfCampaign, $newStart, $newEnd);
313 $campaignOpenByDay = $campaignClass->getOpenByDay($mailIdsOfCampaign, $newStart, $newEnd);
314 $campaignOpenByHour = $campaignClass->getOpenByHour($mailIdsOfCampaign, $newStart, $newEnd);
315
316 if (empty($campaignOpenByMonth) || empty($campaignOpenByDay) || empty($campaignOpenByHour)) {
317 $statsCampaignSelected->hasStats = false;
318
319 return;
320 }
321
322 $urlClickClass = new UrlClickClass();
323 $campaignClickByMonth = $urlClickClass->getAllClickByMailMonth($mailIdsOfCampaign, $newStart, $newEnd);
324 $campaignClickByDay = $urlClickClass->getAllClickByMailDay($mailIdsOfCampaign, $newStart, $newEnd);
325 $campaignClickByHour = $urlClickClass->getAllClickByMailHour($mailIdsOfCampaign, $newStart, $newEnd);
326
327 $statsCampaignSelected->month = $this->getValues('day', 'P1M', $campaignOpenByMonth, $campaignClickByMonth, 'Y-m');
328 $statsCampaignSelected->day = $this->getValues('hour', 'P1D', $campaignOpenByDay, $campaignClickByDay, 'Y-m-d');
329 $statsCampaignSelected->hour = $this->getValues('min', 'PT1H', $campaignOpenByHour, $campaignClickByHour, 'Y-m-d H:i', true);
330
331 $allHour = array_keys($statsCampaignSelected->hour);
332
333 $statsCampaignSelected->startEndDateHour = [];
334 $statsCampaignSelected->startEndDateHour['start'] = $allHour[0];
335 $statsCampaignSelected->startEndDateHour['end'] = end($allHour);
336 }
337
338 public function exportGlobal(): void
339 {
340 $exportType = acym_getVar('string', 'export_type', 'charts');
341
342 $functionName = 'exportGlobal'.ucfirst($exportType);
343
344 if (!method_exists($this, $functionName)) {
345 acym_enqueueMessage(acym_translation('ACYM_EXPORT_METHOD_NOT_FOUND'), 'error');
346 $this->listing();
347
348 return;
349 }
350
351 $this->$functionName();
352 }
353
354 public function prepareOpenTimeChart(array &$data): void
355 {
356 $userStatClass = new UserStatClass();
357 $statsDB = $userStatClass->getOpenTimeStats($this->selectedMailIds);
358
359 if (empty($statsDB['total_open'])) {
360 $data['openTime'] = $userStatClass->getDefaultStat();
361 $data['empty_open'] = true;
362
363 return;
364 }
365 $data['empty_open'] = false;
366
367 $stats = [];
368
369 for ($day = 0 ; $day < 7 ; $day++) {
370 $stats[$day] = [];
371 for ($hour = 0 ; $hour < 8 ; $hour++) {
372 if (empty($statsDB['stats'][$day.'_'.$hour]) || empty($statsDB['total_open'])) {
373 $percentage = 0;
374 } else {
375 $percentage = ($statsDB['stats'][$day.'_'.$hour]->open_total * 100) / $statsDB['total_open'];
376 }
377 $stats[$day][$hour] = round($percentage);
378 }
379 }
380
381 $data['openTime'] = $stats;
382 }
383
384 public function prepareStatByList(array &$data): void
385 {
386 $mailid = (count(array_unique($data['selectedMailid'])) != 1) ? 0 : $data['selectedMailid'][0];
387
388 if (empty($mailid)) {
389 return;
390 }
391
392 $mailSelected = $this->getVarFiltersListing('int', 'mail_id_language', $mailid);
393
394 $mailClass = new MailClass();
395 $mail = $mailClass->getOneById($mailSelected);
396 $mainMailId = empty($mail->parent_id) ? $mail->id : $mail->parent_id;
397
398 $data['lists'] = [];
399 $data['emailsSent'] = [];
400 $data['emailsOpen'] = [];
401 $data['bounces'] = [];
402 $data['unsubscribed'] = [];
403 $data['click'] = [];
404 $data['listsStats'] = [];
405 $data['userPerList'] = [];
406
407 $query = 'SELECT l.id, l.name, l.color FROM #__acym_list l';
408 $query .= ' JOIN #__acym_mail_has_list ml on l.id = ml.list_id';
409 $query .= ' WHERE ml.mail_id = '.$mainMailId;
410
411 $data['lists'] = acym_loadObjectList($query, 'id');
412 $listIds = implode(',', array_keys($data['lists']));
413
414 if (!empty($listIds)) {
415 $query = 'SELECT ul.list_id, SUM(us.sent) as nbSent, SUM(IF(us.open >= 1, 1, 0)) as nbOpen, SUM(us.bounce) as nbBounce, SUM(us.unsubscribe) as nbUnsub from #__acym_user_has_list ul';
416 $query .= ' JOIN #__acym_user_stat us on ul.user_id = us.user_id';
417 $query .= ' WHERE ul.list_id in ('.$listIds.') AND us.mail_id = '.$mailSelected;
418 $query .= ' GROUP BY ul.list_id;';
419 $data['listsStats'] = acym_loadObjectList($query, 'list_id');
420
421 $query = 'SELECT ul.list_id, COUNT(uc.click) as nbClick';
422 $query .= ' FROM #__acym_user_has_list ul ';
423 $query .= ' JOIN #__acym_url_click uc on ul.user_id = uc.user_id';
424 $query .= ' WHERE ul.list_id in ('.$listIds.') AND uc.mail_id = '.$mailSelected;
425 $query .= ' GROUP BY ul.list_id;';
426
427 $data['nbClick'] = acym_loadObjectList($query, 'list_id');
428
429 $query = 'SELECT ul.list_id, COUNT(ul.user_id) as nbUser';
430 $query .= ' FROM #__acym_user_has_list ul';
431 $query .= ' JOIN #__acym_user_stat us ON ul.user_id = us.user_id';
432 $query .= ' WHERE ul.list_id in ('.$listIds.') AND us.mail_id = '.$mailSelected;
433 $query .= ' GROUP BY ul.list_id;';
434
435 $data['userPerList'] = acym_loadObjectList($query, 'list_id');
436 }
437
438 foreach ($data['listsStats'] as $listId => $item) {
439 $data['emailsSent'][$listId] = [
440 'label' => $data['lists'][$listId]->name,
441 'value' => $item->nbSent,
442 'color' => $data['lists'][$listId]->color,
443 ];
444 $data['emailsOpen'][$listId] = [
445 'label' => $data['lists'][$listId]->name,
446 'value' => round(($item->nbOpen * 100) / $item->nbSent),
447 'color' => $data['lists'][$listId]->color,
448 ];
449 $data['bounces'][$listId] = [
450 'label' => $data['lists'][$listId]->name,
451 'value' => round(($item->nbBounce * 100) / $item->nbSent),
452 'color' => $data['lists'][$listId]->color,
453 ];
454 $data['unsubscribed'][$listId] = [
455 'label' => $data['lists'][$listId]->name,
456 'value' => round(($item->nbUnsub * 100) / $item->nbSent),
457 'color' => $data['lists'][$listId]->color,
458 ];
459 }
460
461 foreach ($data['userPerList'] as $listId => $item) {
462 $nbUser = $item->nbUser;
463 $data['click'][$listId] = ['label' => $data['lists'][$listId]->name, 'value' => 0, 'color' => $data['lists'][$listId]->color];
464
465 if ($nbUser > 0 && !empty($data['nbClick'][$listId]->nbClick)) {
466 $data['click'][$listId]['value'] = round(($data['nbClick'][$listId]->nbClick * 100) / $nbUser);
467 }
468 }
469 }
470
471 private function prepareAbTestMails(array &$data): void
472 {
473 if (empty($this->selectedMailIds)) return;
474
475 $mailClass = new MailClass();
476
477 $mailVersions = [];
478
479 if (empty($data['mailInformation']->parent_id)) {
480 $mailVersions = $mailClass->getParentAndChildMails($data['mailInformation']->id);
481 } elseif (!empty($data['mailInformation']->parent_id)) {
482 $parentEmail = $mailClass->getOneById($data['mailInformation']->parent_id);
483 if (empty($parentEmail)) return;
484 $mailVersions = $mailClass->getParentAndChildMails($parentEmail->id);
485 }
486
487 $data['emailVersions'] = [];
488
489 foreach ($mailVersions as $email) {
490 $data['emailVersions'][$email->id] = $email->name.' - '.$email->subject;
491 }
492 }
493
494 private function prepareMultilingualMails(array &$data): void
495 {
496 if (empty($this->selectedMailIds)) return;
497
498 $mailClass = new MailClass();
499
500 $translatedEmails = [];
501
502 if (empty($data['mailInformation']->parent_id)) {
503 $translatedEmails = $mailClass->getTranslationsById($data['mailInformation']->id, true, true);
504 } elseif (!empty($data['mailInformation']->parent_id)) {
505 $parentEmail = $mailClass->getOneById($data['mailInformation']->parent_id);
506 if (empty($parentEmail)) return;
507 $translatedEmails = $mailClass->getTranslationsById($parentEmail->id, true, true);
508 }
509
510 $data['emailTranslations'] = [];
511 $allLanguages = acym_getLanguages();
512
513 foreach ($translatedEmails as $email) {
514 if (!empty($email->language)) {
515 $data['emailTranslations'][$email->id] = empty($allLanguages[$email->language]) ? $email->language
516 : $allLanguages[$email->language]->name;
517 }
518 }
519 }
520
521 private function prepareDevicesStats(array &$data): void
522 {
523 $campaignClass = new CampaignClass();
524 $devicesCampaign = $campaignClass->getDevicesWithCountByMailId($this->selectedMailIds);
525
526 $defaultDataDevices = [
527 'ACYM_MOBILE' => 0,
528 'ACYM_DESKTOP' => 0,
529 'ACYM_OTHER' => 0,
530 'ACYM_UNKNOWN' => 0,
531 ];
532 $data['devices'] = $defaultDataDevices;
533
534 foreach ($devicesCampaign as $oneDevice) {
535 if (empty($oneDevice->number)) {
536 continue;
537 }
538
539 if (empty($oneDevice->device)) {
540 $device = 'ACYM_UNKNOWN';
541 } elseif (in_array($oneDevice->device, array_merge(BrowserDetection::PLATFORMS_MOBILE, BrowserDetection::LEGACY_PLATFORMS_MOBILE))) {
542 $device = 'ACYM_MOBILE';
543 } elseif (in_array($oneDevice->device, array_merge(BrowserDetection::PLATFORMS_DESKTOP, BrowserDetection::LEGACY_PLATFORMS_DESKTOP))) {
544 $device = 'ACYM_DESKTOP';
545 } else {
546 $device = 'ACYM_OTHER';
547 }
548
549 $data['devices'][$device] += $oneDevice->number;
550 }
551
552 if ($data['devices'] === $defaultDataDevices) {
553 $data['devices'] = [];
554 }
555 }
556
557 private function prepareOpenSourcesStats(array &$data): void
558 {
559 $userStatClass = new UserStatClass();
560 $openedFromStats = $userStatClass->getOpenSourcesStats($this->selectedMailIds);
561
562 $data['openedWith'] = [];
563 foreach ($openedFromStats as $oneSource) {
564 if (empty($oneSource->number)) {
565 continue;
566 }
567
568 if (!empty($oneSource->opened_with) && isset(BrowserDetection::LEGACY_OPENED_WITH_MAP[$oneSource->opened_with])) {
569 $oneSource->opened_with = BrowserDetection::LEGACY_OPENED_WITH_MAP[$oneSource->opened_with];
570 }
571
572 if (empty($oneSource->opened_with)) {
573 $oneSource->opened_with = 'ACYM_UNKNOWN';
574 }
575
576 $data['openedWith'][$oneSource->opened_with] = $oneSource->number;
577 }
578 }
579
580 public function preparecharts(array &$data): void
581 {
582 $mailStatClass = new MailStatClass();
583
584 $data['mail'] = $mailStatClass->getSentFailByMailIds($this->selectedMailIds);
585 if (empty($data['mail'])) return;
586
587 $campaignClass = new CampaignClass();
588 $urlClickClass = new UrlClickClass();
589
590 $data['mail']->totalMail = $data['mail']->sent + $data['mail']->fail;
591 $data['mail']->percentageSent = empty($data['mail']->totalMail) ? 0 : number_format(($data['mail']->sent * 100) / $data['mail']->totalMail, 2);
592 $data['mail']->allSent = empty($data['mail']->totalMail)
593 ? acym_translationSprintf('ACYM_X_MAIL_SUCCESSFULLY_SENT_OF_X', 0, 0)
594 : acym_translationSprintf(
595 'ACYM_X_MAIL_SUCCESSFULLY_SENT_OF_X',
596 $data['mail']->sent,
597 $data['mail']->totalMail
598 );
599
600 $openRateCampaign = empty($this->selectedMailIds) ? $campaignClass->getOpenRateAllCampaign() : $campaignClass->getOpenRateCampaigns($this->selectedMailIds);
601 $data['mail']->percentageOpen = empty($openRateCampaign->sent) ? 0 : number_format(($openRateCampaign->open_unique * 100) / $openRateCampaign->sent, 2);
602 $data['mail']->allOpen = empty($openRateCampaign->sent)
603 ? acym_translationSprintf('ACYM_X_MAIL_OPENED_OF_X', 0, 0)
604 : acym_translationSprintf(
605 'ACYM_X_MAIL_OPENED_OF_X',
606 $openRateCampaign->open_unique,
607 $openRateCampaign->sent
608 );
609
610 $clickRateCampaign = $urlClickClass->getNumberUsersClicked($this->selectedMailIds);
611 $data['mail']->percentageClick = empty($data['mail']->sent) ? 0 : number_format(($clickRateCampaign * 100) / $data['mail']->sent, 2);
612 $data['mail']->allClick = empty($data['mail']->sent)
613 ? acym_translationSprintf('ACYM_X_MAIL_CLICKED_OF_X', 0, 0)
614 : acym_translationSprintf(
615 'ACYM_X_MAIL_CLICKED_OF_X',
616 $clickRateCampaign,
617 $data['mail']->sent
618 );
619
620 $bounceRateCampaign = empty($this->selectedMailIds) ? $campaignClass->getBounceRateAllCampaign() : $campaignClass->getBounceRateCampaigns($this->selectedMailIds);
621 $data['mail']->percentageBounce = empty($data['mail']->sent) ? 0 : number_format(($bounceRateCampaign->bounce_unique * 100) / $data['mail']->sent, 2);
622 $data['mail']->allBounce = empty($data['mail']->sent)
623 ? acym_translationSprintf('ACYM_X_BOUNCE_OF_X', 0, 0)
624 : acym_translationSprintf(
625 'ACYM_X_BOUNCE_OF_X',
626 $bounceRateCampaign->bounce_unique,
627 $data['mail']->sent
628 );
629
630 if (!empty($this->selectedMailIds)) {
631 $mailStat = $mailStatClass->getByMailIds($this->selectedMailIds);
632 $data['mail']->percentageUnsub = empty($data['mail']->sent) ? 0 : number_format(($mailStat->unsubscribe_total * 100) / $data['mail']->sent, 2);
633 $data['mail']->allUnsub = empty($data['mail']->sent)
634 ? acym_translationSprintf('ACYM_X_USERS_UNSUBSCRIBED_OF_X', 0, 0)
635 : acym_translationSprintf(
636 'ACYM_X_USERS_UNSUBSCRIBED_OF_X',
637 $mailStat->unsubscribe_total,
638 $data['mail']->sent
639 );
640 }
641
642 $this->prepareDevicesStats($data);
643 $this->prepareOpenSourcesStats($data);
644 $this->prepareLineChart($data['mail'], $this->selectedMailIds);
645 $this->prepareComparedStats($data);
646 }
647
648 private function decode(array &$detailedStats, array $columnsToDecode = ['mail_subject', 'mail_name']): void
649 {
650 foreach ($detailedStats as $oneDetailedStat) {
651 foreach ($columnsToDecode as $column) {
652 if (!empty($oneDetailedStat->$column)) {
653 $oneDetailedStat->$column = acym_utf8Decode($oneDetailedStat->$column);
654 }
655 }
656 }
657 }
658
659 private function prepareLinksDetailsListing(array &$data): void
660 {
661 $data['search'] = $this->getVarFiltersListing('string', 'links_details_search', '');
662 $data['ordering'] = $this->getVarFiltersListing('string', 'links_details_ordering', 'id');
663 $data['orderingSortOrder'] = $this->getVarFiltersListing('string', 'links_details_ordering_sort_order', 'desc');
664
665 if (empty($this->selectedMailIds)) return;
666
667 $pagination = new PaginationHelper();
668 $urlClickClass = new UrlClickClass();
669
670 $detailedStatsPerPage = $pagination->getListLimit();
671 $page = $this->getVarFiltersListing('int', 'links_details_pagination_page', 1);
672
673 $urlClicks = $urlClickClass->getUrlsFromMailsWithDetails(
674 [
675 'ordering' => $data['ordering'],
676 'search' => $data['search'],
677 'detailedStatsPerPage' => $detailedStatsPerPage,
678 'offset' => ($page - 1) * $detailedStatsPerPage,
679 'ordering_sort_order' => $data['orderingSortOrder'],
680 'mail_ids' => $this->selectedMailIds,
681 ]
682 );
683
684 $this->decode($urlClicks['links_details']);
685
686 $pagination->setStatus((int)$urlClicks['total']->total, $page, $detailedStatsPerPage);
687
688 $data['pagination'] = $pagination;
689 $data['links_details'] = $urlClicks['links_details'];
690 $data['query'] = $urlClicks['query'];
691 }
692
693 private function prepareListReceivers(array &$data): void
694 {
695 if (empty($this->selectedMailIds)) return;
696
697 $mailStatClass = new MailStatClass();
698 $mailClass = new MailClass();
699
700 $data['mailStat'] = $mailStatClass->getByMailIds($this->selectedMailIds);
701 $data['lists'] = $mailClass->getAllListsByMailId($this->selectedMailIds);
702 }
703
704 private function exportGlobalFormatted(): void
705 {
706 $exportHelper = new ExportHelper();
707 $data = [];
708 $this->prepareDefaultPageInfo($data);
709 $data['show_date_filters'] = true;
710 $data['page_title'] = false;
711 $timeLinechart = acym_getVar('string', 'time_linechart', 'month');
712
713
714 $this->prepareMailFilter($data);
715 $this->prepareClickStats($data);
716 $this->preparecharts($data);
717 $this->prepareDefaultRoundCharts($data);
718 $this->prepareDefaultLineChart($data);
719 $this->prepareDefaultDevicesChart($data);
720 $this->prepareDefaultBrowsersChart($data);
721
722 $globalDonut = [
723 $data['mail']->percentageSent,
724 $data['mail']->percentageOpen,
725 $data['mail']->percentageClick,
726 $data['mail']->percentageBounce,
727 $data['mail']->percentageUnsub,
728 ];
729 $mailName = empty($this->selectedMailIds) ? acym_translation('ACYM_ALL_MAILS') : $data['mailInformation']->name;
730 $globalLine = $data['mail']->$timeLinechart;
731
732 $exportHelper->exportStatsFormattedCSV($mailName, $globalDonut, $globalLine, $timeLinechart);
733 exit;
734 }
735
736 private function exportGlobalFull(): void
737 {
738 $exportHelper = new ExportHelper();
739 $data = [];
740 $this->prepareDefaultPageInfo($data);
741
742 $where = '';
743 if (!empty($this->selectedMailIds)) {
744 $where = 'WHERE mail_id IN ('.implode(',', $this->selectedMailIds).')';
745 }
746
747 $columnsMailStat = acym_getColumns('mail_stat');
748 $columnsToExport = [];
749
750 $columnsToExport['mail.subject'] = acym_translation('ACYM_EMAIL_SUBJECT');
751 foreach ($columnsMailStat as $column) {
752 if ($column === 'mail_id') {
753 continue;
754 }
755
756 $translation = acym_translation('ACYM_'.strtoupper($column).'_COLUMN_STAT');
757 if ($column === 'send_date') {
758 $translation = acym_translation('ACYM_SEND_DATE');
759 } elseif ($column === 'click_total') {
760 $translation = acym_translation('ACYM_TOTAL_CLICKS');
761 } elseif ($column === 'click_unique') {
762 $translation = acym_translation('ACYM_UNIQUE_CLICKS');
763 }
764
765 $columnsToExport['mailstat.'.$column] = $translation;
766 }
767
768 $query = 'SELECT '.implode(', ', array_keys($columnsToExport)).' FROM #__acym_mail_stat AS mailstat LEFT JOIN #__acym_mail AS mail ON mail.id = mailstat.mail_id '.$where;
769 $exportHelper->exportStatsFullCSV($query, $columnsToExport);
770 exit;
771 }
772
773 private function prepareComparedStats(array &$data): void
774 {
775 if (empty($data['totalSubscribers']) || empty($data['mail']->percentageOpen) || empty($data['mail']->percentageClick) || empty($data['mail']->percentageBounce)) {
776 return;
777 }
778
779 $currentDate = acym_date('now', 'Y-m-d');
780
781 $newStats = [
782 'date' => $currentDate,
783 'value' => [
784 'totalSubscribers' => $data['totalSubscribers'],
785 'openRate' => $data['mail']->percentageOpen,
786 'clickRate' => $data['mail']->percentageClick,
787 'bounceRate' => $data['mail']->percentageBounce,
788 'totalNewSubscribers' => $data['totalSubscribers'],
789 ],
790 ];
791
792 $keys = [
793 'totalSubscribersHistory' => true,
794 'percentageOpenHistory' => false,
795 'percentageClickHistory' => false,
796 'percentageBounceHistory' => false,
797 'totalNewSubscribersHistory' => false,
798 ];
799
800 $oldEvolutionData = json_decode($this->config->get('statsEvolution', '{}'), true);
801
802 foreach ($keys as $key => $isPercentage) {
803 $history = json_decode($this->config->get($key, '[]'), true);
804
805 if (!empty($history) && end($history)['date'] === $currentDate) {
806 continue;
807 }
808
809 $history[] = ['date' => $currentDate, 'value' => $newStats['value'][$this->getStatKey($key)]];
810
811 if (count($history) > 30) {
812 array_shift($history);
813 }
814
815 $this->config->save([$key => json_encode($history)]);
816
817 $evolutionKey = $this->getEvolutionKey($key);
818 $oldEvolutionData[$evolutionKey] = $this->calculateEvolution($history, $isPercentage);
819 }
820
821 $this->config->save(['statsEvolution' => json_encode($oldEvolutionData)]);
822
823 if (!empty($data)) {
824 $this->updateDashboardData($data);
825 }
826 }
827
828 private function getStatKey(string $key): ?string
829 {
830 return [
831 'totalSubscribersHistory' => 'totalSubscribers',
832 'percentageOpenHistory' => 'openRate',
833 'percentageClickHistory' => 'clickRate',
834 'percentageBounceHistory' => 'bounceRate',
835 'totalNewSubscribersHistory' => 'totalNewSubscribers',
836 ][$key] ?? null;
837 }
838
839 private function getEvolutionKey(string $key): ?string
840 {
841 return [
842 'totalSubscribersHistory' => 'totalSubscribersEvolution',
843 'percentageOpenHistory' => 'openRateEvolution',
844 'percentageClickHistory' => 'clickRateEvolution',
845 'percentageBounceHistory' => 'bounceRateEvolution',
846 'totalNewSubscribersHistory' => 'totalNewSubscribersEvolution',
847 ][$key] ?? null;
848 }
849
850 private function calculateEvolution(array $history, bool $isPercentage = false): ?float
851 {
852 if (empty($history)) {
853 return null;
854 }
855
856 $oldValue = $history[0]['value'] ?? 0;
857 $newValue = end($history)['value'] ?? 0;
858
859 if ($oldValue === 0) {
860 return null;
861 }
862
863 if ($isPercentage) {
864 return round((($newValue - $oldValue) / $oldValue) * 100, 2);
865 } else {
866 return round($newValue - $oldValue, 2);
867 }
868 }
869
870 private function updateDashboardData(array &$data): void
871 {
872 $evolutionData = json_decode($this->config->get('statsEvolution', '{}'), true);
873
874 if (!empty($evolutionData)) {
875 $data['totalSubscribersEvolution'] = $evolutionData['totalSubscribersEvolution'] ?? null;
876 $data['openRateEvolution'] = $evolutionData['openRateEvolution'] ?? null;
877 $data['clickRateEvolution'] = $evolutionData['clickRateEvolution'] ?? null;
878 $data['bounceRateEvolution'] = $evolutionData['bounceRateEvolution'] ?? null;
879 $data['totalNewSubscribersEvolution'] = $evolutionData['totalNewSubscribersEvolution'] ?? null;
880 }
881 }
882 }
883