PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.0
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 +94 -41 11.0.0 → 11.1.0 View file →
@@ -241,9 +241,9 @@
241 241 $startDate = $clickDate;
242 242 }
243 243 }
244 244
245 - $lastOpen = end($campaignOpens);
245 + $lastOpen = empty($campaignOpens) ? null : end($campaignOpens);
246 246 $endDate = $lastOpen->open_date ?? null;
247 247 if (!empty($campaignClicks)) {
248 248 $lastClick = end($campaignClicks);
249 249 $clickDate = $lastClick->date_click ?? null;
@@ -288,20 +288,22 @@
288 288 }
289 289
290 290 public function prepareLineChart(object &$statsCampaignSelected, array $mailIdsOfCampaign, string $newStart = '', string $newEnd = ''): void
291 291 {
292 - $campaignClass = new CampaignClass();
293 292 $statsCampaignSelected->hasStats = true;
293 + $anonymousStats = !empty($this->config->get('anonymous_stats', 0));
294 294
295 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;
296 + if ($anonymousStats) {
297 + $mailStatClass = new MailStatClass();
298 + $campaignOpenByMonth = $mailStatClass->getAnonOpensByFormat($mailIdsOfCampaign, '%Y-%m', $newStart, $newEnd);
299 + $campaignOpenByDay = $mailStatClass->getAnonOpensByFormat($mailIdsOfCampaign, '%Y-%m-%d', $newStart, $newEnd);
300 + $campaignOpenByHour = $mailStatClass->getAnonOpensByFormat($mailIdsOfCampaign, '%Y-%m-%d %H:00:00', $newStart, $newEnd);
301 + } else {
302 + $campaignClass = new CampaignClass();
303 + $campaignOpenByMonth = $campaignClass->getOpenByMonth($mailIdsOfCampaign, $newStart, $newEnd);
304 + $campaignOpenByDay = $campaignClass->getOpenByDay($mailIdsOfCampaign, $newStart, $newEnd);
305 + $campaignOpenByHour = $campaignClass->getOpenByHour($mailIdsOfCampaign, $newStart, $newEnd);
304 306 }
305 307
306 308 $urlClickClass = new UrlClickClass();
307 309 $campaignClickByMonth = $urlClickClass->getAllClickByMailMonth($mailIdsOfCampaign, $newStart, $newEnd);
@@ -307,8 +309,17 @@
307 309 $campaignClickByMonth = $urlClickClass->getAllClickByMailMonth($mailIdsOfCampaign, $newStart, $newEnd);
308 310 $campaignClickByDay = $urlClickClass->getAllClickByMailDay($mailIdsOfCampaign, $newStart, $newEnd);
309 311 $campaignClickByHour = $urlClickClass->getAllClickByMailHour($mailIdsOfCampaign, $newStart, $newEnd);
310 312
313 + // In anonymous mode a click doesn't register an open, and in any mode a link can be clicked without the open pixel firing: gating on opens alone wrongly hid the whole chart.
314 + $hasOpens = !empty($campaignOpenByMonth) && !empty($campaignOpenByDay) && !empty($campaignOpenByHour);
315 + $hasClicks = !empty($campaignClickByMonth) && !empty($campaignClickByDay) && !empty($campaignClickByHour);
316 + if (!$hasOpens && !$hasClicks) {
317 + $statsCampaignSelected->hasStats = false;
318 +
319 + return;
320 + }
321 +
311 322 $statsCampaignSelected->month = $this->getValues('day', 'P1M', $campaignOpenByMonth, $campaignClickByMonth, 'Y-m');
312 323 $statsCampaignSelected->day = $this->getValues('hour', 'P1D', $campaignOpenByDay, $campaignClickByDay, 'Y-m-d');
313 324 $statsCampaignSelected->hour = $this->getValues('min', 'PT1H', $campaignOpenByHour, $campaignClickByHour, 'Y-m-d H:i', true);
314 325
@@ -342,9 +353,14 @@
342 353
343 354 public function prepareOpenTimeChart(array &$data): void
344 355 {
345 356 $userStatClass = new UserStatClass();
346 - $statsDB = $userStatClass->getOpenTimeStats($this->selectedMailIds);
357 + if (!empty($this->config->get('anonymous_stats', 0))) {
358 + $mailStatClass = new MailStatClass();
359 + $statsDB = $mailStatClass->getAnonOpenTimeStats($this->selectedMailIds);
360 + } else {
361 + $statsDB = $userStatClass->getOpenTimeStats($this->selectedMailIds);
362 + }
347 363
348 364 if (empty($statsDB['total_open'])) {
349 365 $data['openTime'] = $userStatClass->getDefaultStat();
350 366 $data['empty_open'] = true;
@@ -372,20 +388,17 @@
372 388 }
373 389
374 390 public function prepareStatByList(array &$data): void
375 391 {
376 - $mailid = (count(array_unique($data['selectedMailid'])) != 1) ? 0 : $data['selectedMailid'][0];
392 + $selectedMailIds = array_unique($data['selectedMailid']);
393 + $mailid = count($selectedMailIds) !== 1 ? 0 : (int)reset($selectedMailIds);
377 394
378 395 if (empty($mailid)) {
379 396 return;
380 397 }
381 398
382 - $mailSelected = $this->getVarFiltersListing('int', 'mail_id_language', $mailid);
399 + $mailSelected = (int)$this->getVarFiltersListing('int', 'mail_id_language', $mailid);
383 400
384 - $mailClass = new MailClass();
385 - $mail = $mailClass->getOneById($mailSelected);
386 - $mainMailId = empty($mail->parent_id) ? $mail->id : $mail->parent_id;
387 -
388 401 $data['lists'] = [];
389 402 $data['emailsSent'] = [];
390 403 $data['emailsOpen'] = [];
391 404 $data['bounces'] = [];
@@ -393,12 +406,26 @@
393 406 $data['click'] = [];
394 407 $data['listsStats'] = [];
395 408 $data['userPerList'] = [];
396 409
410 + $mailClass = new MailClass();
411 + $mail = $mailClass->getOneById($mailSelected);
412 +
413 + if (empty($mail) || !in_array($mailid, [$mail->id, $mail->parent_id])) {
414 + $mailSelected = $mailid;
415 + $mail = $mailClass->getOneById($mailSelected);
416 + }
417 +
418 + if (empty($mail)) {
419 + return;
420 + }
421 +
422 + $mainMailId = empty($mail->parent_id) ? $mail->id : $mail->parent_id;
423 +
397 424 // get lists from the selected campaign
398 425 $query = 'SELECT l.id, l.name, l.color FROM #__acym_list l';
399 426 $query .= ' JOIN #__acym_mail_has_list ml on l.id = ml.list_id';
400 - $query .= ' WHERE ml.mail_id = '.$mainMailId;
427 + $query .= ' WHERE ml.mail_id = '.intval($mainMailId);
401 428
402 429 $data['lists'] = acym_loadObjectList($query, 'id');
403 430 $listIds = implode(',', array_keys($data['lists']));
404 431
@@ -518,10 +545,15 @@
518 545 }
519 546
520 547 private function prepareDevicesStats(array &$data): void
521 548 {
522 - $campaignClass = new CampaignClass();
523 - $devicesCampaign = $campaignClass->getDevicesWithCountByMailId($this->selectedMailIds);
549 + if (!empty($this->config->get('anonymous_stats', 0))) {
550 + $mailStatClass = new MailStatClass();
551 + $devicesCampaign = $mailStatClass->getAnonDevices($this->selectedMailIds);
552 + } else {
553 + $campaignClass = new CampaignClass();
554 + $devicesCampaign = $campaignClass->getDevicesWithCountByMailId($this->selectedMailIds);
555 + }
524 556
525 557 $defaultDataDevices = [
526 558 'ACYM_MOBILE' => 0,
527 559 'ACYM_DESKTOP' => 0,
@@ -554,10 +586,15 @@
554 586 }
555 587
556 588 private function prepareOpenSourcesStats(array &$data): void
557 589 {
558 - $userStatClass = new UserStatClass();
559 - $openedFromStats = $userStatClass->getOpenSourcesStats($this->selectedMailIds);
590 + if (!empty($this->config->get('anonymous_stats', 0))) {
591 + $mailStatClass = new MailStatClass();
592 + $openedFromStats = $mailStatClass->getAnonOpenSources($this->selectedMailIds);
593 + } else {
594 + $userStatClass = new UserStatClass();
595 + $openedFromStats = $userStatClass->getOpenSourcesStats($this->selectedMailIds);
596 + }
560 597
561 598 $data['openedWith'] = [];
562 599 foreach ($openedFromStats as $oneSource) {
563 600 if (empty($oneSource->number)) {
@@ -596,29 +633,45 @@
596 633 $data['mail']->sent,
597 634 $data['mail']->totalMail
598 635 );
599 636
637 + // Anonymized mode has no per-subscriber data: show total opens/clicks instead of unique counts
638 + $anonymousStats = !empty($this->config->get('anonymous_stats', 0));
639 + $data['anonymousStats'] = $anonymousStats;
640 +
641 + if ($anonymousStats) {
642 + $cumulatedTotals = $mailStatClass->getCumulatedStatsByMailIds($this->selectedMailIds);
643 + $openCount = (int)($cumulatedTotals->open ?? 0);
644 + $clickCount = (int)($cumulatedTotals->click_unique ?? 0);
645 + $openSent = (int)$data['mail']->sent;
646 + } else {
647 + $openRateCampaign = empty($this->selectedMailIds) ? $campaignClass->getOpenRateAllCampaign() : $campaignClass->getOpenRateCampaigns($this->selectedMailIds);
648 + $openCount = (int)$openRateCampaign->open_unique;
649 + $openSent = (int)$openRateCampaign->sent;
650 + $clickCount = $urlClickClass->getNumberUsersClicked($this->selectedMailIds);
651 + }
652 +
600 653 //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 - );
654 + // In anonymous mode $openCount is a total number of opens (not a unique-user count), so use a wording that reflects the total number of times mails were opened
655 + $openLabelKey = $anonymousStats ? 'ACYM_X_MAIL_OPENED_TIMES_OF_X' : 'ACYM_X_MAIL_OPENED_OF_X';
656 + $data['mail']->openCount = $openCount;
657 + // In anonymous mode the count is a total (not a unique), so the rate can exceed 100%: cap it to keep a meaningful percentage
658 + $openRate = empty($openSent) ? 0 : ($openCount * 100) / $openSent;
659 + $data['mail']->percentageOpen = number_format($anonymousStats ? min($openRate, 100) : $openRate, 2);
660 + $data['mail']->allOpen = empty($openSent)
661 + ? acym_translationSprintf($openLabelKey, 0, 0)
662 + : acym_translationSprintf($openLabelKey, $openCount, $openSent);
610 663
611 664 //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);
665 + // In anonymous mode $clickCount is a total (aggregated with user_id = 0), not a unique-user count, so use a wording that reflects total clicks
666 + $clickLabelKey = $anonymousStats ? 'ACYM_X_CLICKS_REGISTERED_OF_X' : 'ACYM_X_MAIL_CLICKED_OF_X';
667 + $data['mail']->clickCount = $clickCount;
668 + // In anonymous mode the count is a total (not a unique), so the rate can exceed 100%: cap it to keep a meaningful percentage
669 + $clickRate = empty($data['mail']->sent) ? 0 : ($clickCount * 100) / $data['mail']->sent;
670 + $data['mail']->percentageClick = number_format($anonymousStats ? min($clickRate, 100) : $clickRate, 2);
614 671 $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 - );
672 + ? acym_translationSprintf($clickLabelKey, 0, 0)
673 + : acym_translationSprintf($clickLabelKey, $clickCount, $data['mail']->sent);
621 674
622 675 //bounce rate
623 676 $bounceRateCampaign = empty($this->selectedMailIds) ? $campaignClass->getBounceRateAllCampaign() : $campaignClass->getBounceRateCampaigns($this->selectedMailIds);
624 677 $data['mail']->percentageBounce = empty($data['mail']->sent) ? 0 : number_format(($bounceRateCampaign->bounce_unique * 100) / $data['mail']->sent, 2);
@@ -666,10 +719,10 @@
666 719
667 720 private function prepareLinksDetailsListing(array &$data): void
668 721 {
669 722 $data['search'] = $this->getVarFiltersListing('string', 'links_details_search', '');
670 - $data['ordering'] = $this->getVarFiltersListing('string', 'links_details_ordering', 'id');
671 - $data['orderingSortOrder'] = $this->getVarFiltersListing('string', 'links_details_ordering_sort_order', 'desc');
723 + $data['ordering'] = $this->getVarFiltersListing('cmd', 'links_details_ordering', 'id');
724 + $data['orderingSortOrder'] = $this->getVarFiltersListing('cmd', 'links_details_ordering_sort_order', 'desc');
672 725
673 726 if (empty($this->selectedMailIds)) return;
674 727
675 728 $pagination = new PaginationHelper();