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_test_api', [$this, 'handleApi']);
$this->wp->addAction('admin_post_feca_mailshots_test_render_ui', [$this, 'handleRenderUi']);
$this->wp->addAction('admin_post_feca_mailshots_test_send_ui', [$this, 'handleSendUi']);
}
public function registerMenu(): void
{
$this->wp->addSubmenuPage('feca-mailshot', 'Mailshot Test', 'Mailshot Test', self::CAPABILITY, 'feca-mailshots-test', [$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');
$preview = ['ok' => false, 'rows' => [], 'errors' => ['Select a mailshot.']];
if ($selectedMailshotId > 0) {
$preview = $this->runService()->previewRecipients($selectedMailshotId, 100);
}
$rows = is_array($preview['rows'] ?? null) ? $preview['rows'] : [];
$selectedRecipientIndex = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
if ($selectedRecipientIndex < -1) {
$selectedRecipientIndex = 0;
}
$defaultEmail = $this->runService()->defaultTestEmail()['default_test_email'] ?? '';
$testEmail = (string) ($this->wp->requestParam('test_email', (string) $defaultEmail) ?? $defaultEmail);
$result = $this->result();
$action = htmlspecialchars($this->wp->adminUrl('admin-post.php'));
echo '
Mailshot Test
';
echo $this->renderAdminUiStyles();
echo '
Render with a selected recipient context, then optionally send one test email.
';
if ($result !== null) {
$ok = !empty($result['ok']);
$bannerClass = $ok ? 'feca-banner-success' : 'feca-banner-error';
echo '
';
echo '
' . ($ok ? 'Test action succeeded.' : 'Test action failed.') . '';
if (!empty($result['errors']) && is_array($result['errors'])) {
echo '
' . htmlspecialchars(implode('; ', $result['errors'])) . '
';
}
if (!empty($result['warnings']) && is_array($result['warnings'])) {
echo '
Warnings: ' . htmlspecialchars(implode('; ', $result['warnings'])) . '
';
}
if (!empty($result['sent_to'])) {
echo '
Sent to: ' . htmlspecialchars((string) $result['sent_to']) . '
';
}
if (!empty($result['sent_at'])) {
echo '
Sent at: ' . htmlspecialchars((string) $result['sent_at']) . '
';
}
if (($result['ui_action'] ?? '') === 'render' && !empty($result['ok']) && is_array($result['rendered'] ?? null)) {
echo '
';
}
echo '
';
}
echo '
';
echo '
1. Select Mailshot
';
echo '
';
echo '
';
if (!empty($preview['errors'])) {
echo '
' . htmlspecialchars(implode('; ', $preview['errors'])) . '
';
}
echo '
';
echo '';
if ($rows !== []) {
$sampleColumns = $this->sampleColumns($rows, 4);
echo '
';
echo '
Recipient Sample (First 20)
';
echo '
';
echo '
';
}
if ($result !== null && ($result['ui_action'] ?? '') === 'render' && !empty($result['ok']) && is_array($result['rendered'] ?? null)) {
$rendered = $result['rendered'];
$recipient = $this->selectedRecipientSummary($rows, $selectedRecipientIndex);
$recipientText = $recipient['email'] ?? '';
if (($recipient['key'] ?? '') !== '' && strcasecmp((string) ($recipient['key'] ?? ''), (string) ($recipient['email'] ?? '')) !== 0) {
$recipientText .= ($recipientText !== '' ? ' | ' : '') . (string) $recipient['key'];
}
if ($recipientText === '') {
$recipientText = '(recipient unavailable)';
}
$subject = (string) ($rendered['subject'] ?? '');
$messageHtml = (string) ($rendered['message'] ?? '');
$pdfHtml = (string) ($rendered['pdf_attachment'] ?? '');
echo '
';
echo '
';
echo '
Render Preview
';
echo '
Recipient: ' . htmlspecialchars($recipientText) . '
';
echo '
Subject: ' . htmlspecialchars($subject) . '
';
echo '
';
echo '
Message (HTML)
';
echo '
PDF Attachment (HTML)
';
echo '
';
echo '
';
echo '
';
echo '';
}
echo '
';
}
public function handleApi(): void
{
if (!$this->enforceCapabilityOrJson(self::CAPABILITY)) {
return;
}
$op = (string) ($this->wp->requestParam('op', '') ?? '');
$service = $this->runService();
try {
if ($op === 'default_test_email') {
$this->wp->sendJson(['ok' => true] + $service->defaultTestEmail());
return;
}
if ($op === 'render_test') {
if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
return;
}
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
if ($idx < 0) {
$this->wp->sendJson(['ok' => false, 'errors' => ['Choose a specific recipient row for Render Test.']]);
return;
}
$this->wp->sendJson($service->renderTest($mailshotId, $idx));
return;
}
if ($op === 'send_test') {
if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
return;
}
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
$to = (string) ($this->wp->requestParam('test_email', '') ?? '');
if ($idx < 0) {
$this->wp->sendJson($service->sendTestAll($mailshotId, $to));
return;
}
$this->wp->sendJson($service->sendTest($mailshotId, $idx, $to));
return;
}
$this->wp->sendJson(['ok' => false, 'error' => 'Unknown op'], 400);
} catch (\Throwable $e) {
$this->wp->sendJson(['ok' => false, 'error' => $e->getMessage()], 500);
}
}
public function handleRenderUi(): void
{
if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
return;
}
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
if ($idx < 0) {
$result = ['ok' => false, 'errors' => ['Choose a specific recipient row for Render Test.']];
$result['ui_action'] = 'render';
$this->wp->updateOption(self::RESULT_OPTION_KEY, $result);
$this->redirect($mailshotId, $idx, (string) ($this->wp->requestParam('test_email', '') ?? ''));
return;
}
try {
$result = $this->runService()->renderTest($mailshotId, $idx);
} catch (\Throwable $e) {
$result = ['ok' => false, 'errors' => ['Render test failed: ' . $e->getMessage()]];
}
$result['ui_action'] = 'render';
$this->wp->updateOption(self::RESULT_OPTION_KEY, $result);
$this->redirect($mailshotId, $idx, (string) ($this->wp->requestParam('test_email', '') ?? ''));
}
public function handleSendUi(): void
{
if (!$this->enforceMutationGuardOrJson(self::CAPABILITY, self::NONCE_ACTION)) {
return;
}
$mailshotId = (int) ($this->wp->requestParam('mailshot_id', '0') ?? '0');
$idx = (int) ($this->wp->requestParam('recipient_index', '0') ?? '0');
$email = (string) ($this->wp->requestParam('test_email', '') ?? '');
try {
if ($idx < 0) {
$result = $this->runService()->sendTestAll($mailshotId, $email);
} else {
$result = $this->runService()->sendTest($mailshotId, $idx, $email);
}
} catch (\Throwable $e) {
$result = ['ok' => false, 'errors' => ['Send test failed: ' . $e->getMessage()]];
}
unset($result['rendered']);
$result['ui_action'] = 'send';
$this->wp->updateOption(self::RESULT_OPTION_KEY, $result);
$this->redirect($mailshotId, $idx, $email);
}
private function redirect(int $mailshotId, int $recipientIndex, string $testEmail): void
{
$url = $this->wp->adminUrl('admin.php?page=feca-mailshots-test&mailshot_id=' . $mailshotId . '&recipient_index=' . $recipientIndex . '&test_email=' . rawurlencode($testEmail));
if (!headers_sent()) {
header('Location: ' . $url, true, 302);
exit;
}
}
/** @return array