464 lines
15 KiB
PHP
464 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Copyright seit 2024 Webshop System
|
|
*
|
|
* Newsletter API Controller für das Webshop-System
|
|
*
|
|
* @author Webshop System
|
|
* @license GPL v3
|
|
*/
|
|
|
|
namespace App\API\Controllers;
|
|
|
|
use Doctrine\DBAL\DriverManager;
|
|
use Doctrine\DBAL\Exception;
|
|
|
|
class NewsletterApiController extends ApiController
|
|
{
|
|
/**
|
|
* Newsletter abonnieren
|
|
*/
|
|
public function subscribe()
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
$this->sendError('Method not allowed', 405);
|
|
}
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
|
|
if (!$input) {
|
|
$this->sendError('Invalid JSON input', 400);
|
|
}
|
|
|
|
if (empty($input['email'])) {
|
|
$this->sendError('Email is required', 400);
|
|
}
|
|
|
|
if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) {
|
|
$this->sendError('Invalid email address', 400);
|
|
}
|
|
|
|
try {
|
|
// Prüfen ob bereits abonniert
|
|
$stmt = $this->conn->prepare('
|
|
SELECT id, active FROM ws_newsletter_subscriber
|
|
WHERE email = ?
|
|
');
|
|
$stmt->execute([$input['email']]);
|
|
$existing = $stmt->fetchAssociative();
|
|
|
|
if ($existing) {
|
|
if ($existing['active']) {
|
|
$this->sendError('Email is already subscribed', 409);
|
|
} else {
|
|
// Reaktivieren
|
|
$stmt = $this->conn->prepare('
|
|
UPDATE ws_newsletter_subscriber
|
|
SET active = 1, updated_at = NOW()
|
|
WHERE id = ?
|
|
');
|
|
$stmt->execute([$existing['id']]);
|
|
|
|
$this->sendResponse([
|
|
'success' => true,
|
|
'message' => 'Newsletter subscription reactivated'
|
|
]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Neues Abonnement erstellen
|
|
$stmt = $this->conn->prepare('
|
|
INSERT INTO ws_newsletter_subscriber (email, first_name, last_name,
|
|
preferences, active, created_at)
|
|
VALUES (?, ?, ?, ?, 1, NOW())
|
|
');
|
|
|
|
$stmt->execute([
|
|
$input['email'],
|
|
$input['first_name'] ?? '',
|
|
$input['last_name'] ?? '',
|
|
json_encode($input['preferences'] ?? [])
|
|
]);
|
|
|
|
$subscriberId = $this->conn->lastInsertId();
|
|
|
|
// Willkommens-E-Mail senden
|
|
$this->sendWelcomeEmail($input['email'], $input['first_name'] ?? '');
|
|
|
|
$this->sendResponse([
|
|
'success' => true,
|
|
'data' => [
|
|
'subscriber_id' => $subscriberId
|
|
],
|
|
'message' => 'Newsletter subscription successful'
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->sendError('Failed to subscribe: ' . $e->getMessage(), 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Newsletter abbestellen
|
|
*/
|
|
public function unsubscribe()
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
$this->sendError('Method not allowed', 405);
|
|
}
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
|
|
if (!$input) {
|
|
$this->sendError('Invalid JSON input', 400);
|
|
}
|
|
|
|
if (empty($input['email'])) {
|
|
$this->sendError('Email is required', 400);
|
|
}
|
|
|
|
if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) {
|
|
$this->sendError('Invalid email address', 400);
|
|
}
|
|
|
|
try {
|
|
$stmt = $this->conn->prepare('
|
|
UPDATE ws_newsletter_subscriber
|
|
SET active = 0, unsubscribed_at = NOW()
|
|
WHERE email = ?
|
|
');
|
|
$stmt->execute([$input['email']]);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
$this->sendError('Email not found in subscribers', 404);
|
|
}
|
|
|
|
$this->sendResponse([
|
|
'success' => true,
|
|
'message' => 'Newsletter unsubscribed successfully'
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->sendError('Failed to unsubscribe: ' . $e->getMessage(), 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Newsletter-Präferenzen aktualisieren
|
|
*/
|
|
public function updatePreferences($subscriberId)
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'PUT') {
|
|
$this->sendError('Method not allowed', 405);
|
|
}
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
|
|
if (!$input) {
|
|
$this->sendError('Invalid JSON input', 400);
|
|
}
|
|
|
|
try {
|
|
$updateFields = [];
|
|
$params = [];
|
|
|
|
if (isset($input['first_name'])) {
|
|
$updateFields[] = 'first_name = ?';
|
|
$params[] = $input['first_name'];
|
|
}
|
|
|
|
if (isset($input['last_name'])) {
|
|
$updateFields[] = 'last_name = ?';
|
|
$params[] = $input['last_name'];
|
|
}
|
|
|
|
if (isset($input['preferences'])) {
|
|
$updateFields[] = 'preferences = ?';
|
|
$params[] = json_encode($input['preferences']);
|
|
}
|
|
|
|
if (empty($updateFields)) {
|
|
$this->sendError('No fields to update', 400);
|
|
}
|
|
|
|
$updateFields[] = 'updated_at = NOW()';
|
|
$params[] = $subscriberId;
|
|
|
|
$sql = 'UPDATE ws_newsletter_subscriber SET ' . implode(', ', $updateFields) . ' WHERE id = ?';
|
|
$stmt = $this->conn->prepare($sql);
|
|
$stmt->execute($params);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
$this->sendError('Subscriber not found', 404);
|
|
}
|
|
|
|
$this->sendResponse([
|
|
'success' => true,
|
|
'message' => 'Preferences updated successfully'
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->sendError('Failed to update preferences: ' . $e->getMessage(), 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Newsletter-Templates abrufen
|
|
*/
|
|
public function getTemplates()
|
|
{
|
|
try {
|
|
$stmt = $this->conn->prepare('
|
|
SELECT id, name, subject, content, active, created_at
|
|
FROM ws_newsletter_template
|
|
WHERE active = 1
|
|
ORDER BY name ASC
|
|
');
|
|
$stmt->execute();
|
|
$templates = $stmt->fetchAllAssociative();
|
|
|
|
$this->sendResponse([
|
|
'success' => true,
|
|
'data' => $templates
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->sendError('Failed to fetch templates: ' . $e->getMessage(), 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Newsletter senden
|
|
*/
|
|
public function sendNewsletter()
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
$this->sendError('Method not allowed', 405);
|
|
}
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
|
|
if (!$input) {
|
|
$this->sendError('Invalid JSON input', 400);
|
|
}
|
|
|
|
if (empty($input['template_id']) || empty($input['subject'])) {
|
|
$this->sendError('Template ID and subject are required', 400);
|
|
}
|
|
|
|
try {
|
|
// Template abrufen
|
|
$stmt = $this->conn->prepare('
|
|
SELECT * FROM ws_newsletter_template
|
|
WHERE id = ? AND active = 1
|
|
');
|
|
$stmt->execute([$input['template_id']]);
|
|
$template = $stmt->fetchAssociative();
|
|
|
|
if (!$template) {
|
|
$this->sendError('Template not found', 404);
|
|
}
|
|
|
|
// Aktive Abonnenten abrufen
|
|
$stmt = $this->conn->prepare('
|
|
SELECT id, email, first_name, last_name, preferences
|
|
FROM ws_newsletter_subscriber
|
|
WHERE active = 1
|
|
');
|
|
$stmt->execute();
|
|
$subscribers = $stmt->fetchAllAssociative();
|
|
|
|
if (empty($subscribers)) {
|
|
$this->sendError('No active subscribers found', 400);
|
|
}
|
|
|
|
$sentCount = 0;
|
|
$failedCount = 0;
|
|
|
|
foreach ($subscribers as $subscriber) {
|
|
try {
|
|
$this->sendNewsletterEmail($subscriber, $template, $input['subject']);
|
|
$sentCount++;
|
|
} catch (Exception $e) {
|
|
$failedCount++;
|
|
}
|
|
}
|
|
|
|
// Newsletter-Log erstellen
|
|
$stmt = $this->conn->prepare('
|
|
INSERT INTO ws_newsletter_log (template_id, subject, sent_count,
|
|
failed_count, created_at)
|
|
VALUES (?, ?, ?, ?, NOW())
|
|
');
|
|
$stmt->execute([
|
|
$input['template_id'],
|
|
$input['subject'],
|
|
$sentCount,
|
|
$failedCount
|
|
]);
|
|
|
|
$this->sendResponse([
|
|
'success' => true,
|
|
'data' => [
|
|
'sent_count' => $sentCount,
|
|
'failed_count' => $failedCount,
|
|
'total_subscribers' => count($subscribers)
|
|
],
|
|
'message' => 'Newsletter sent successfully'
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->sendError('Failed to send newsletter: ' . $e->getMessage(), 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Newsletter-Statistiken
|
|
*/
|
|
public function getStats()
|
|
{
|
|
try {
|
|
// Abonnenten-Statistiken
|
|
$stmt = $this->conn->prepare('
|
|
SELECT
|
|
COUNT(*) as total_subscribers,
|
|
COUNT(CASE WHEN active = 1 THEN 1 END) as active_subscribers,
|
|
COUNT(CASE WHEN active = 0 THEN 1 END) as inactive_subscribers
|
|
FROM ws_newsletter_subscriber
|
|
');
|
|
$stmt->execute();
|
|
$subscriberStats = $stmt->fetchAssociative();
|
|
|
|
// Newsletter-Log-Statistiken
|
|
$stmt = $this->conn->prepare('
|
|
SELECT
|
|
COUNT(*) as total_campaigns,
|
|
SUM(sent_count) as total_sent,
|
|
SUM(failed_count) as total_failed,
|
|
AVG(sent_count) as avg_sent_per_campaign
|
|
FROM ws_newsletter_log
|
|
');
|
|
$stmt->execute();
|
|
$campaignStats = $stmt->fetchAssociative();
|
|
|
|
// Template-Statistiken
|
|
$stmt = $this->conn->prepare('
|
|
SELECT COUNT(*) as total_templates
|
|
FROM ws_newsletter_template
|
|
WHERE active = 1
|
|
');
|
|
$stmt->execute();
|
|
$templateStats = $stmt->fetchAssociative();
|
|
|
|
$this->sendResponse([
|
|
'success' => true,
|
|
'data' => [
|
|
'subscribers' => $subscriberStats,
|
|
'campaigns' => $campaignStats,
|
|
'templates' => $templateStats
|
|
]
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
$this->sendError('Failed to get newsletter stats: ' . $e->getMessage(), 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Willkommens-E-Mail senden
|
|
*/
|
|
private function sendWelcomeEmail($email, $firstName)
|
|
{
|
|
$subject = 'Willkommen beim Newsletter!';
|
|
$message = "
|
|
<html>
|
|
<body>
|
|
<h2>Willkommen beim Newsletter!</h2>
|
|
<p>Hallo " . htmlspecialchars($firstName ?: 'Lieber Kunde') . ",</p>
|
|
<p>Vielen Dank für Ihr Abonnement unseres Newsletters. Sie erhalten nun regelmäßig Informationen über:</p>
|
|
<ul>
|
|
<li>Neue Produkte und Angebote</li>
|
|
<li>Exklusive Rabatte und Aktionen</li>
|
|
<li>Interessante Artikel und Tipps</li>
|
|
</ul>
|
|
<p>Falls Sie den Newsletter nicht mehr erhalten möchten, können Sie sich jederzeit <a href='" . getenv('BASE_URL') . "/unsubscribe'>hier abmelden</a>.</p>
|
|
<p>Mit freundlichen Grüßen<br>Ihr Webshop-Team</p>
|
|
</body>
|
|
</html>
|
|
";
|
|
|
|
$this->sendEmail($email, $subject, $message);
|
|
}
|
|
|
|
/**
|
|
* Newsletter-E-Mail senden
|
|
*/
|
|
private function sendNewsletterEmail($subscriber, $template, $subject)
|
|
{
|
|
$preferences = json_decode($subscriber['preferences'], true) ?: [];
|
|
|
|
// Template personalisieren
|
|
$content = $template['content'];
|
|
$content = str_replace('{first_name}', $subscriber['first_name'], $content);
|
|
$content = str_replace('{last_name}', $subscriber['last_name'], $content);
|
|
$content = str_replace('{email}', $subscriber['email'], $content);
|
|
|
|
// Präferenzen-basierte Inhalte
|
|
if (!empty($preferences['categories'])) {
|
|
$categoryContent = $this->getCategoryContent($preferences['categories']);
|
|
$content = str_replace('{category_content}', $categoryContent, $content);
|
|
}
|
|
|
|
$this->sendEmail($subscriber['email'], $subject, $content);
|
|
}
|
|
|
|
/**
|
|
* Kategorie-Inhalte abrufen
|
|
*/
|
|
private function getCategoryContent($categories)
|
|
{
|
|
try {
|
|
$placeholders = str_repeat('?,', count($categories) - 1) . '?';
|
|
$stmt = $this->conn->prepare("
|
|
SELECT name, description
|
|
FROM ws_category
|
|
WHERE id IN ($placeholders) AND active = 1
|
|
LIMIT 3
|
|
");
|
|
$stmt->execute($categories);
|
|
$categoryData = $stmt->fetchAllAssociative();
|
|
|
|
$content = '<h3>Ihre bevorzugten Kategorien:</h3>';
|
|
foreach ($categoryData as $category) {
|
|
$content .= '<h4>' . htmlspecialchars($category['name']) . '</h4>';
|
|
$content .= '<p>' . htmlspecialchars($category['description']) . '</p>';
|
|
}
|
|
|
|
return $content;
|
|
|
|
} catch (Exception $e) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* E-Mail senden
|
|
*/
|
|
private function sendEmail($to, $subject, $message)
|
|
{
|
|
$headers = [
|
|
'MIME-Version: 1.0',
|
|
'Content-type: text/html; charset=UTF-8',
|
|
'From: ' . (getenv('MAIL_FROM') ?: 'noreply@webshop.local'),
|
|
'Reply-To: ' . (getenv('MAIL_REPLY_TO') ?: 'support@webshop.local'),
|
|
'X-Mailer: Webshop System'
|
|
];
|
|
|
|
$result = mail($to, $subject, $message, implode("\r\n", $headers));
|
|
|
|
if (!$result) {
|
|
throw new Exception('Failed to send email');
|
|
}
|
|
}
|
|
}
|