feat: add Newsletter Campaigns backend infrastructure

- Add CampaignManager.php with CPT registration, CRUD, batch sending
- Add CampaignsController.php with 8 REST endpoints (list, create, get, update, delete, send, test, preview)
- Register newsletter_campaign event in EventRegistry for email template
- Initialize CampaignManager in Bootstrap.php
- Register routes in Routes.php
This commit is contained in:
Dwindi Ramadhana
2025-12-31 14:58:57 +07:00
parent 2dbc43a4eb
commit 65dd847a66
5 changed files with 824 additions and 0 deletions

View File

@@ -0,0 +1,320 @@
<?php
/**
* Campaigns REST Controller
*
* REST API endpoints for newsletter campaigns
*
* @package WooNooW\API
*/
namespace WooNooW\API;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
use WooNooW\Core\Campaigns\CampaignManager;
class CampaignsController {
const API_NAMESPACE = 'woonoow/v1';
/**
* Register REST routes
*/
public static function register_routes() {
// List campaigns
register_rest_route(self::API_NAMESPACE, '/campaigns', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_campaigns'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
// Create campaign
register_rest_route(self::API_NAMESPACE, '/campaigns', [
'methods' => 'POST',
'callback' => [__CLASS__, 'create_campaign'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
// Get single campaign
register_rest_route(self::API_NAMESPACE, '/campaigns/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_campaign'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
// Update campaign
register_rest_route(self::API_NAMESPACE, '/campaigns/(?P<id>\d+)', [
'methods' => 'PUT',
'callback' => [__CLASS__, 'update_campaign'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
// Delete campaign
register_rest_route(self::API_NAMESPACE, '/campaigns/(?P<id>\d+)', [
'methods' => 'DELETE',
'callback' => [__CLASS__, 'delete_campaign'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
// Send campaign
register_rest_route(self::API_NAMESPACE, '/campaigns/(?P<id>\d+)/send', [
'methods' => 'POST',
'callback' => [__CLASS__, 'send_campaign'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
// Send test email
register_rest_route(self::API_NAMESPACE, '/campaigns/(?P<id>\d+)/test', [
'methods' => 'POST',
'callback' => [__CLASS__, 'send_test_email'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
// Preview campaign
register_rest_route(self::API_NAMESPACE, '/campaigns/(?P<id>\d+)/preview', [
'methods' => 'GET',
'callback' => [__CLASS__, 'preview_campaign'],
'permission_callback' => [__CLASS__, 'check_admin_permission'],
]);
}
/**
* Check admin permission
*/
public static function check_admin_permission() {
return current_user_can('manage_options');
}
/**
* Get all campaigns
*/
public static function get_campaigns(WP_REST_Request $request) {
$campaigns = CampaignManager::get_all();
return new WP_REST_Response([
'success' => true,
'data' => $campaigns,
]);
}
/**
* Create campaign
*/
public static function create_campaign(WP_REST_Request $request) {
$data = [
'title' => $request->get_param('title'),
'subject' => $request->get_param('subject'),
'content' => $request->get_param('content'),
'status' => $request->get_param('status') ?: 'draft',
'scheduled_at' => $request->get_param('scheduled_at'),
];
$campaign_id = CampaignManager::create($data);
if (is_wp_error($campaign_id)) {
return new WP_REST_Response([
'success' => false,
'error' => $campaign_id->get_error_message(),
], 400);
}
$campaign = CampaignManager::get($campaign_id);
return new WP_REST_Response([
'success' => true,
'data' => $campaign,
], 201);
}
/**
* Get single campaign
*/
public static function get_campaign(WP_REST_Request $request) {
$campaign_id = (int) $request->get_param('id');
$campaign = CampaignManager::get($campaign_id);
if (!$campaign) {
return new WP_REST_Response([
'success' => false,
'error' => __('Campaign not found', 'woonoow'),
], 404);
}
return new WP_REST_Response([
'success' => true,
'data' => $campaign,
]);
}
/**
* Update campaign
*/
public static function update_campaign(WP_REST_Request $request) {
$campaign_id = (int) $request->get_param('id');
$data = [];
if ($request->has_param('title')) {
$data['title'] = $request->get_param('title');
}
if ($request->has_param('subject')) {
$data['subject'] = $request->get_param('subject');
}
if ($request->has_param('content')) {
$data['content'] = $request->get_param('content');
}
if ($request->has_param('status')) {
$data['status'] = $request->get_param('status');
}
if ($request->has_param('scheduled_at')) {
$data['scheduled_at'] = $request->get_param('scheduled_at');
}
$result = CampaignManager::update($campaign_id, $data);
if (is_wp_error($result)) {
return new WP_REST_Response([
'success' => false,
'error' => $result->get_error_message(),
], 400);
}
$campaign = CampaignManager::get($campaign_id);
return new WP_REST_Response([
'success' => true,
'data' => $campaign,
]);
}
/**
* Delete campaign
*/
public static function delete_campaign(WP_REST_Request $request) {
$campaign_id = (int) $request->get_param('id');
$result = CampaignManager::delete($campaign_id);
if (!$result) {
return new WP_REST_Response([
'success' => false,
'error' => __('Failed to delete campaign', 'woonoow'),
], 400);
}
return new WP_REST_Response([
'success' => true,
'message' => __('Campaign deleted', 'woonoow'),
]);
}
/**
* Send campaign
*/
public static function send_campaign(WP_REST_Request $request) {
$campaign_id = (int) $request->get_param('id');
$result = CampaignManager::send($campaign_id);
if (!$result['success']) {
return new WP_REST_Response([
'success' => false,
'error' => $result['error'],
], 400);
}
return new WP_REST_Response([
'success' => true,
'message' => sprintf(
__('Campaign sent to %d recipients (%d failed)', 'woonoow'),
$result['sent'],
$result['failed']
),
'sent' => $result['sent'],
'failed' => $result['failed'],
'total' => $result['total'],
]);
}
/**
* Send test email
*/
public static function send_test_email(WP_REST_Request $request) {
$campaign_id = (int) $request->get_param('id');
$email = sanitize_email($request->get_param('email'));
if (!is_email($email)) {
return new WP_REST_Response([
'success' => false,
'error' => __('Invalid email address', 'woonoow'),
], 400);
}
$result = CampaignManager::send_test($campaign_id, $email);
if (!$result) {
return new WP_REST_Response([
'success' => false,
'error' => __('Failed to send test email', 'woonoow'),
], 400);
}
return new WP_REST_Response([
'success' => true,
'message' => sprintf(__('Test email sent to %s', 'woonoow'), $email),
]);
}
/**
* Preview campaign
*/
public static function preview_campaign(WP_REST_Request $request) {
$campaign_id = (int) $request->get_param('id');
$campaign = CampaignManager::get($campaign_id);
if (!$campaign) {
return new WP_REST_Response([
'success' => false,
'error' => __('Campaign not found', 'woonoow'),
], 404);
}
// Use reflection to call private render method or make it public
// For now, return a simple preview
$renderer = \WooNooW\Core\Notifications\EmailRenderer::instance();
$template = $renderer->get_template_settings('newsletter_campaign', 'customer');
$content = $campaign['content'];
$subject = $campaign['subject'] ?: $campaign['title'];
if ($template) {
$content = str_replace('{content}', $campaign['content'], $template['body']);
$content = str_replace('{campaign_title}', $campaign['title'], $content);
}
// Replace placeholders
$site_name = get_bloginfo('name');
$content = str_replace(['{site_name}', '{store_name}'], $site_name, $content);
$content = str_replace('{site_url}', home_url(), $content);
$content = str_replace('{subscriber_email}', 'subscriber@example.com', $content);
$content = str_replace('{unsubscribe_url}', '#unsubscribe', $content);
$content = str_replace('{current_date}', date_i18n(get_option('date_format')), $content);
$content = str_replace('{current_year}', date('Y'), $content);
// Render with design template
$design_path = $renderer->get_design_template();
if (file_exists($design_path)) {
$content = $renderer->render_html($design_path, $content, $subject, [
'site_name' => $site_name,
'site_url' => home_url(),
]);
}
return new WP_REST_Response([
'success' => true,
'subject' => $subject,
'html' => $content,
]);
}
}