runServiceFactory = $runServiceFactory;
$this->mailshotServiceFactory = $mailshotServiceFactory;
$this->wp = $wp;
}
public function register(): void
{
$this->wp->addAction('admin_menu', [$this, 'registerMenu']);
$this->wp->addAction('admin_post_feca_mailshots_run_api', [$this, 'handleApi']);
$this->wp->addAction('admin_post_feca_mailshots_run_execute_ui', [$this, 'handleRunUi']);
$this->wp->addAction('admin_post_feca_mailshots_retry_failed_ui', [$this, 'handleRetryFailedUi']);
$this->wp->addAction('admin_post_feca_mailshots_retry_recipient_ui', [$this, 'handleRetryRecipientUi']);
}
public function registerMenu(): void
{
$this->wp->addSubmenuPage('feca-mailshot', 'Run Mailshot', 'Run Mailshot', self::CAPABILITY, 'feca-mailshots-run', [$this, 'render']);
}
public function render(): void
{
if (!$this->wp->currentUserCan(self::CAPABILITY)) {
echo 'Permission denied';
return;
}
$mailshots = $this->mailshotService()->list();
$selectedMailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
if ($selectedMailshotId <= 0 && $mailshots !== []) {
$selectedMailshotId = (int) ($mailshots[0]['id'] ?? 0);
}
$result = $this->result();
$lastRunRows = $selectedMailshotId > 0 ? $this->mailshotService()->lastRun($selectedMailshotId) : [];
$action = htmlspecialchars($this->wp->adminUrl('admin-post.php'));
echo '
Run Mailshot
';
echo '
Execute full sends and retry failures from the current mailshot last-run rows.
';
if ($result !== null) {
$ok = !empty($result['ok']);
$bg = $ok ? '#f1f8e9' : '#ffebee';
$border = $ok ? '#8bc34a' : '#ef9a9a';
echo '
';
echo '
' . ($ok ? 'Run action completed.' : 'Run action failed.') . '';
if ($ok) {
echo '
attempted=' . (int) ($result['attempted'] ?? 0)
. ', sent=' . (int) ($result['sent'] ?? 0)
. ', failed=' . (int) ($result['failed'] ?? 0)
. ', warnings=' . (int) ($result['warnings'] ?? 0)
. ', skipped=' . (int) ($result['skipped'] ?? 0)
. '
';
}
if (!empty($result['errors']) && is_array($result['errors'])) {
echo '
' . htmlspecialchars(implode('; ', $result['errors'])) . '
';
}
echo '
Show technical details
' . htmlspecialchars(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '
';
echo '
';
}
echo '
';
echo '
1. Select Mailshot
';
echo '';
echo '';
echo '
';
echo '
';
echo '
3. Last Run Rows
';
if ($lastRunRows === []) {
echo '
No rows in mailshot_last_run for this mailshot.
';
} else {
echo '
| Recipient Key | Email | Status | Attempts | Error/Warning | Retry |
';
foreach ($lastRunRows as $row) {
$recipientKey = (string) ($row['recipient_key'] ?? '');
$status = (string) ($row['status'] ?? '');
$msg = (string) (($row['error_message'] ?? '') ?: ($row['warning_message'] ?? ''));
echo '';
echo '| ' . htmlspecialchars($recipientKey) . ' | ';
echo '' . htmlspecialchars((string) ($row['recipient_email_last'] ?? '')) . ' | ';
echo '' . htmlspecialchars($status) . ' | ';
echo '' . (int) ($row['attempt_count'] ?? 0) . ' | ';
echo '' . htmlspecialchars($msg) . ' | ';
echo '';
if ($status === 'failed') {
echo '';
}
echo ' | ';
echo '
';
}
echo '
';
}
echo '
';
echo '
';
}
public function handleApi(): void
{
if (!$this->wp->currentUserCan(self::CAPABILITY)) {
$this->wp->sendJson(['ok' => false, 'error' => 'Permission denied'], 403);
return;
}
$op = (string) ($this->wp->requestParam('op', '') ?? '');
$service = $this->runService();
try {
if ($op === 'run_mailshot') {
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$this->wp->sendJson($service->runMailshot($mailshotId));
return;
}
if ($op === 'retry_failed') {
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$this->wp->sendJson($service->retryFailed($mailshotId));
return;
}
if ($op === 'retry_recipient') {
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$recipientKey = (string) ($this->wp->requestParam('recipient_key', '') ?? '');
$this->wp->sendJson($service->retryRecipient($mailshotId, $recipientKey));
return;
}
$this->wp->sendJson(['ok' => false, 'error' => 'Unknown op'], 400);
} catch (\Throwable $e) {
$this->wp->sendJson(['ok' => false, 'error' => $e->getMessage()], 500);
}
}
public function handleRunUi(): void
{
if (!$this->wp->currentUserCan(self::CAPABILITY)) {
$this->wp->sendJson(['ok' => false, 'error' => 'Permission denied'], 403);
return;
}
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$result = $this->runService()->runMailshot($mailshotId);
$this->wp->updateOption(self::RESULT_OPTION_KEY, $result);
$this->redirect($mailshotId);
}
public function handleRetryFailedUi(): void
{
if (!$this->wp->currentUserCan(self::CAPABILITY)) {
$this->wp->sendJson(['ok' => false, 'error' => 'Permission denied'], 403);
return;
}
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$result = $this->runService()->retryFailed($mailshotId);
$this->wp->updateOption(self::RESULT_OPTION_KEY, $result);
$this->redirect($mailshotId);
}
public function handleRetryRecipientUi(): void
{
if (!$this->wp->currentUserCan(self::CAPABILITY)) {
$this->wp->sendJson(['ok' => false, 'error' => 'Permission denied'], 403);
return;
}
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$recipientKey = (string) ($this->wp->requestParam('recipient_key', '') ?? '');
$result = $this->runService()->retryRecipient($mailshotId, $recipientKey);
$this->wp->updateOption(self::RESULT_OPTION_KEY, $result);
$this->redirect($mailshotId);
}
private function redirect(int $mailshotId): void
{
$url = $this->wp->adminUrl('admin.php?page=feca-mailshots-run&mailshot_id=' . $mailshotId);
if (!headers_sent()) {
header('Location: ' . $url, true, 302);
exit;
}
}
/** @return array|null */
private function result(): ?array
{
$raw = $this->wp->getOption(self::RESULT_OPTION_KEY, null);
return is_array($raw) ? $raw : null;
}
private function runService(): MailshotRunService
{
return ($this->runServiceFactory)();
}
private function mailshotService(): MailshotService
{
return ($this->mailshotServiceFactory)();
}
}