feat: complete Newsletter Campaigns Phase 1

- Add default campaign email template to DefaultTemplates.php
- Add toggle settings (campaign_scheduling, subscriber_limit_enabled)
- Add public unsubscribe endpoint with secure token verification
- Update CampaignManager to use NewsletterController unsubscribe URLs
- Add generate_unsubscribe_url() helper for email templates
This commit is contained in:
Dwindi Ramadhana
2025-12-31 21:17:59 +07:00
parent 3d5191aab3
commit d7505252ac
4 changed files with 138 additions and 6 deletions

View File

@@ -56,6 +56,23 @@ class NewsletterController {
return current_user_can('manage_options');
},
]);
// Public unsubscribe endpoint (no auth needed, uses token)
register_rest_route(self::API_NAMESPACE, '/newsletter/unsubscribe', [
'methods' => 'GET',
'callback' => [__CLASS__, 'unsubscribe'],
'permission_callback' => '__return_true',
'args' => [
'email' => [
'required' => true,
'type' => 'string',
],
'token' => [
'required' => true,
'type' => 'string',
],
],
]);
}
public static function get_template(WP_REST_Request $request) {
@@ -197,4 +214,78 @@ class NewsletterController {
],
], 200);
}
/**
* Handle unsubscribe request
*/
public static function unsubscribe(WP_REST_Request $request) {
$email = sanitize_email(urldecode($request->get_param('email')));
$token = sanitize_text_field($request->get_param('token'));
// Verify token
$expected_token = self::generate_unsubscribe_token($email);
if (!hash_equals($expected_token, $token)) {
return new WP_REST_Response([
'success' => false,
'message' => __('Invalid unsubscribe link', 'woonoow'),
], 400);
}
// Get subscribers
$subscribers = get_option('woonoow_newsletter_subscribers', []);
$found = false;
foreach ($subscribers as &$sub) {
if (isset($sub['email']) && $sub['email'] === $email) {
$sub['status'] = 'unsubscribed';
$sub['unsubscribed_at'] = current_time('mysql');
$found = true;
break;
}
}
if (!$found) {
return new WP_REST_Response([
'success' => false,
'message' => __('Email not found', 'woonoow'),
], 404);
}
update_option('woonoow_newsletter_subscribers', $subscribers);
do_action('woonoow_newsletter_unsubscribed', $email);
// Return HTML page for nice UX
$site_name = get_bloginfo('name');
$html = sprintf(
'<!DOCTYPE html><html><head><title>%s</title><style>body{font-family:system-ui,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;background:#f5f5f5;}.box{background:white;padding:40px;border-radius:8px;text-align:center;box-shadow:0 2px 10px rgba(0,0,0,0.1);max-width:400px;}h1{color:#333;margin-bottom:16px;}p{color:#666;}</style></head><body><div class="box"><h1>✓ Unsubscribed</h1><p>You have been unsubscribed from %s newsletter.</p></div></body></html>',
__('Unsubscribed', 'woonoow'),
esc_html($site_name)
);
header('Content-Type: text/html; charset=utf-8');
echo $html;
exit;
}
/**
* Generate secure unsubscribe token
*/
private static function generate_unsubscribe_token($email) {
$secret = wp_salt('auth');
return hash_hmac('sha256', $email, $secret);
}
/**
* Generate unsubscribe URL for email templates
*/
public static function generate_unsubscribe_url($email) {
$token = self::generate_unsubscribe_token($email);
$base_url = rest_url('woonoow/v1/newsletter/unsubscribe');
return add_query_arg([
'email' => urlencode($email),
'token' => $token,
], $base_url);
}
}