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