<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Cron\Workers\StatsNotifications;
if (!defined('ABSPATH')) exit;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
use MailPoet\Config\Renderer;
use MailPoet\Cron\Workers\SimpleWorker;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\ScheduledTaskEntity;
use MailPoet\Mailer\MailerFactory;
use MailPoet\Mailer\MetaInfo;
use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Statistics\NewsletterStatistics;
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
use MailPoet\Settings\SettingsController;
use MailPoet\Settings\TrackingConfig;
use MailPoet\WP\DateTime as WpDateTime;
use MailPoet\WP\Functions as WPFunctions;
use MailPoet\WPCOM\DotcomHelperFunctions;
use MailPoetVendor\Carbon\Carbon;
class AutomatedEmails extends SimpleWorker {
const TASK_TYPE = 'stats_notification_automated_emails';
/** @var MailerFactory */
private $mailerFactory;
/** @var SettingsController */
private $settings;
/** @var Renderer */
private $renderer;
/** @var MetaInfo */
private $mailerMetaInfo;
/** @var NewslettersRepository */
private $repository;
/** @var NewsletterStatisticsRepository */
private $newsletterStatisticsRepository;
/** @var TrackingConfig */
private $trackingConfig;
/** @var WpDateTime */
private $wpDateTime;
/** @var DotcomHelperFunctions */
private $dotcomHelperFunctions;
/** @var AutomationStorage */
private $automationStorage;
public function __construct(
MailerFactory $mailerFactory,
Renderer $renderer,
SettingsController $settings,
NewslettersRepository $repository,
NewsletterStatisticsRepository $newsletterStatisticsRepository,
MetaInfo $mailerMetaInfo,
TrackingConfig $trackingConfig,
DotcomHelperFunctions $dotcomHelperFunctions,
AutomationStorage $automationStorage
) {
parent::__construct();
$this->mailerFactory = $mailerFactory;
$this->settings = $settings;
$this->renderer = $renderer;
$this->mailerMetaInfo = $mailerMetaInfo;
$this->repository = $repository;
$this->newsletterStatisticsRepository = $newsletterStatisticsRepository;
$this->trackingConfig = $trackingConfig;
$this->dotcomHelperFunctions = $dotcomHelperFunctions;
$this->automationStorage = $automationStorage;
$this->wpDateTime = new WpDateTime();
}
public function checkProcessingRequirements() {
$settings = $this->settings->get(Worker::SETTINGS_KEY);
if (!is_array($settings)) {
return false;
}
if (!isset($settings['automated'])) {
return false;
}
if (!is_string($settings['address'] ?? null) || trim($settings['address']) === '') {
return false;
}
if (!$this->trackingConfig->isEmailTrackingEnabled()) {
return false;
}
return (bool)$settings['automated'];
}
public function processTaskStrategy(ScheduledTaskEntity $task, $timer) {
try {
$settings = $this->settings->get(Worker::SETTINGS_KEY);
$newsletters = $this->getNewsletters();
if ($newsletters) {
$extraParams = [
'meta' => $this->mailerMetaInfo->getStatsNotificationMetaInfo(),
];
$this->mailerFactory->getDefaultMailer()->send($this->constructNewsletter($newsletters, $settings), $settings['address'], $extraParams);
}
} catch (\Exception $e) {
if (WP_DEBUG) {
throw $e;
}
}
return true;
}
/**
* @param array<int, array{newsletter: NewsletterEntity, statistics: NewsletterStatistics}> $newsletters
*/
private function constructNewsletter(array $newsletters, array $settings): array {
$context = $this->prepareContext($newsletters, $settings);
if ($this->dotcomHelperFunctions->isGarden()) {
$subject = __('Your monthly automation stats are in!', 'mailpoet');
} else {
$subject = __('Your monthly stats are in!', 'mailpoet');
}
return [
'subject' => $subject,
'body' => [
'html' => $this->renderer->render('emails/statsNotificationAutomatedEmails.html', $context),
'text' => $this->renderer->render('emails/statsNotificationAutomatedEmails.txt', $context),
],
];
}
/**
* @return array<int, array{newsletter: NewsletterEntity, statistics: NewsletterStatistics}>
*/
protected function getNewsletters(): array {
$result = [];
$newsletters = $this->repository->findActiveByTypes(
[NewsletterEntity::TYPE_AUTOMATIC, NewsletterEntity::TYPE_WELCOME, NewsletterEntity::TYPE_AUTOMATION]
);
$activeAutomationNewsletterIds = $this->getActiveAutomationNewsletterIds();
foreach ($newsletters as $newsletter) {
if (
$newsletter->getType() === NewsletterEntity::TYPE_AUTOMATION
&& !in_array($newsletter->getId(), $activeAutomationNewsletterIds, true)
) {
continue;
}
$statistics = $this->newsletterStatisticsRepository->getStatistics($newsletter);
if ($statistics->getTotalSentCount()) {
$result[] = [
'statistics' => $statistics,
'newsletter' => $newsletter,
];
}
}
return $result;
}
/** @return int[] */
private function getActiveAutomationNewsletterIds(): array {
$ids = [];
$automations = $this->automationStorage->getAutomations([Automation::STATUS_ACTIVE]);
foreach ($automations as $automation) {
foreach ($automation->getSteps() as $step) {
if ($step->getKey() === SendEmailAction::KEY) {
$args = $step->getArgs();
if (isset($args['email_id'])) {
$ids[] = (int)$args['email_id'];
}
}
}
}
return $ids;
}
/**
* @param array<int, array{newsletter: NewsletterEntity, statistics: NewsletterStatistics}> $newsletters
* @return array
*/
private function prepareContext(array $newsletters, array $settings = []): array {
$context = [
'linkSettings' => WPFunctions::get()->applyFilters(
'mailpoet_stats_notification_link_settings',
WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-settings#basics')
),
'blogName' => WPFunctions::get()->getBloginfo('name'),
'recipientFirstName' => $this->getRecipientFirstName($settings['address'] ?? ''),
'newsletters' => [],
];
foreach ($newsletters as $row) {
$statistics = $row['statistics'];
$newsletter = $row['newsletter'];
$totalSentCount = $statistics->getTotalSentCount() ?: 1;
$clicked = ($statistics->getClickCount() * 100) / $totalSentCount;
$opened = ($statistics->getOpenCount() * 100) / $totalSentCount;
$machineOpened = ($statistics->getMachineOpenCount() * 100) / $totalSentCount;
$unsubscribed = ($statistics->getUnsubscribeCount() * 100) / $totalSentCount;
$bounced = ($statistics->getBounceCount() * 100) / $totalSentCount;
$context['newsletters'][] = [
'linkStats' => WPFunctions::get()->applyFilters(
'mailpoet_stats_notification_link_stats',
WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-newsletters#/stats/' . $newsletter->getId()),
$newsletter->getId()
),
'clicked' => $clicked,
'opened' => $opened,
'machineOpened' => $machineOpened,
'unsubscribed' => $unsubscribed,
'bounced' => $bounced,
'subject' => $newsletter->getSubject(),
];
}
return $context;
}
private function getRecipientFirstName(string $email): string {
if (empty($email)) {
return '';
}
$user = WPFunctions::get()->getUserBy('email', $email);
if (!$user) {
return '';
}
$firstName = $user->first_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
if (!empty($firstName)) {
return $firstName;
}
$displayName = $user->display_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
return !empty($displayName) ? $displayName : '';
}
public function getNextRunDate() {
$currentDateTime = $this->wpDateTime->getCurrentDateTime();
$date = Carbon::instance($currentDateTime)->millisecond(0);
// Get first Monday of next month at midnight
$nextMonday = $date->endOfMonth()->next(Carbon::MONDAY)->startOfDay();
// Add random time across the entire day (0-23 hours, 0-59 minutes, 0-59 seconds)
return $nextMonday
->addHours(rand(0, 23))
->addMinutes(rand(0, 59))
->addSeconds(rand(0, 59));
}
}