From 48721c85ebcfa973ee81719d2467ca80e4253dc9 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 01 May 2026 17:30:03 +0000
Subject: [PATCH] =Edmonton Ink hard test begins! Real testing of the managers and reset routes will commence. So far, just ensuring our classes are all loaded correctly: Site() and its sub-classes Membership, Login, etc. Care should be taken to load conditionally on 'init', as we finish defining most settings by 'plugins_loaded' at priority 5
---
inc/rest/routes/ReferralRoutes.php | 800 ++++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 588 insertions(+), 212 deletions(-)
diff --git a/inc/rest/routes/ReferralRoutes.php b/inc/rest/routes/ReferralRoutes.php
index d7f097f..2ef284a 100644
--- a/inc/rest/routes/ReferralRoutes.php
+++ b/inc/rest/routes/ReferralRoutes.php
@@ -1,7 +1,12 @@
<?php
namespace JVBase\rest\routes;
-use JVBase\rest\RestRouteManager;
+use JVBase\base\Site;
+use JVBase\importers\JaneAppClientImporter;
+use JVBase\importers\JaneAppSalesImporter;
+use JVBase\managers\CustomTable;
+use JVBase\rest\Rest;
+use JVBase\rest\Route;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
@@ -13,301 +18,672 @@
/**
* REST API routes for referral system
*/
-class ReferralRoutes extends RestRouteManager
+class ReferralRoutes extends Rest
{
+ protected CustomTable $referrals;
+ protected CustomTable $rewards;
+ protected CustomTable $treatments;
public function __construct()
{
- $this->route = 'referrals';
+ $this->cacheName = 'referrals';
+ $this->cacheTtl = (int)HOUR_IN_SECONDS;
parent::__construct();
+
+ $this->referrals = CustomTable::for('referrals');
+ $this->rewards = CustomTable::for('referral_rewards');
+ $this->treatments = CustomTable::for('referral_treatments');
+
+ add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
}
public function registerRoutes(): void
{
- // Get user's referrals
- register_rest_route($this->namespace, "/{$this->route}", [
- 'methods' => 'GET',
- 'callback' => [$this, 'getUserReferrals'],
- 'permission_callback' => [$this, 'checkPermission']
- ]);
+ // Main referrals endpoint - list and manage referrals
+ Route::for('referrals')
+ ->get([$this, 'getReferrals'])
+ ->args([
+ 'user' => 'integer',
+ 'status' => 'string|enum:all,pending,consulted,treated,unused,registered,completed|default:all',
+ 'date_start' => 'string',
+ 'date_end' => 'string',
+ 'limit' => 'integer|default:50',
+ 'offset' => 'integer|default:0',
+ 'search' => 'string'
+ ])
+ ->rateLimit()
+ ->post([$this, 'handleAction'])
+ ->args([
+ 'action' => 'string|required|enum:invite,consulted,treated,remove,resend'
+ ])
+ ->auth('user')
+ ->rateLimit(10)
+ ->register();
- // Get or create referral code
- register_rest_route($this->namespace, "/{$this->route}/code", [
- 'methods' => 'GET',
- 'callback' => [$this, 'getReferralCode'],
- 'permission_callback' => [$this, 'checkPermission']
- ]);
+ // Referral code endpoint
+ Route::for('referrals/code')
+ ->get([$this, 'getCode'])
+ ->args(['user' => 'integer'])
+ ->auth('user')
+ ->rateLimit(30)
+ ->post([$this, 'validateCode'])
+ ->args(['code' => 'string|required'])
+ ->auth('public')
+ ->rateLimit(10)
+ ->register();
- // Update referral code
- register_rest_route($this->namespace, "/{$this->route}/code", [
- 'methods' => 'POST',
- 'callback' => [$this, 'updateReferralCode'],
- 'permission_callback' => [$this, 'checkPermission'],
- 'args' => [
- 'code' => [
- 'required' => true,
- 'type' => 'string',
- 'validate_callback' => function($param) {
- return preg_match('/^[A-Z0-9]+$/i', $param);
- }
- ]
- ]
- ]);
+ // Stats endpoint
+ Route::for('referrals/stats')
+ ->get([$this, 'getStats'])
+ ->args(['user' => 'integer'])
+ ->auth('user')
+ ->rateLimit(30)
+ ->register();
- // Track referral click (public endpoint)
- register_rest_route($this->namespace, "/{$this->route}/track", [
- 'methods' => 'POST',
- 'callback' => [$this, 'trackReferralClick'],
- 'permission_callback' => '__return_true',
- 'args' => [
- 'code' => [
- 'required' => true,
- 'type' => 'string'
- ]
- ]
- ]);
+ // Settings endpoint (admin only)
+ Route::for('referrals/settings')
+ ->get([$this, 'getSettings'])
+ ->post([$this, 'updateSettings'])
+ ->auth('admin')
+ ->rateLimit(10)
+ ->register();
- // Mark referral as treated
- register_rest_route($this->namespace, "/{$this->route}/(?P<id>\d+)/treat", [
- 'methods' => 'POST',
- 'callback' => [$this, 'markAsTreated'],
- 'permission_callback' => function() {
- return current_user_can('manage_options');
- },
- 'args' => [
- 'id' => [
- 'required' => true,
- 'validate_callback' => function($param) {
- return is_numeric($param);
- }
- ]
- ]
- ]);
+ // CSV Upload endpoints (admin only)
+ Route::for('referrals/upload-clients')
+ ->post([$this, 'handleClientUpload'])
+ ->auth('admin')
+ ->rateLimit(3)
+ ->register();
- // Get user stats
- register_rest_route($this->namespace, "/{$this->route}/stats", [
- 'methods' => 'GET',
- 'callback' => [$this, 'getUserStats'],
- 'permission_callback' => [$this, 'checkPermission']
- ]);
-
- // Get top referrers (admin only)
- register_rest_route($this->namespace, "/{$this->route}/leaderboard", [
- 'methods' => 'GET',
- 'callback' => [$this, 'getTopReferrers'],
- 'permission_callback' => function() {
- return current_user_can('manage_options');
- },
- 'args' => [
- 'period' => [
- 'default' => 'week',
- 'enum' => ['day', 'week', 'month', 'all']
- ],
- 'limit' => [
- 'default' => 10,
- 'type' => 'integer'
- ]
- ]
- ]);
-
- // Get/Update referral settings (admin only)
- register_rest_route($this->namespace, "/{$this->route}/settings", [
- [
- 'methods' => 'GET',
- 'callback' => [$this, 'getSettings'],
- 'permission_callback' => function() {
- return current_user_can('manage_options');
- }
- ],
- [
- 'methods' => 'POST',
- 'callback' => [$this, 'updateSettings'],
- 'permission_callback' => function() {
- return current_user_can('manage_options');
- }
- ]
- ]);
- }
-
- public function checkPermission(WP_REST_Request $request): bool
- {
- return is_user_logged_in();
+ Route::for('referrals/upload-sales')
+ ->post([$this, 'handleSalesUpload'])
+ ->auth('admin')
+ ->rateLimit(3)
+ ->register();
}
/**
- * Get user's referrals
+ * GET /referrals
+ * Get referrals with optional filters
+ * - User gets their own referrals
+ * - Admin with no user param gets all referrals
*/
- public function getUserReferrals(WP_REST_Request $request): WP_REST_Response
+ public function getReferrals(WP_REST_Request $request): WP_REST_Response
{
- $user_id = get_current_user_id();
+ $user_id = $request->get_param('user');
+ // Determine scope: admin without user param gets all referrals
+ if (!$user_id) {
+ $current_user_id = get_current_user_id();
+ if (current_user_can('manage_options')) {
+ return $this->getAllReferrals($request);
+ }
+ $user_id = $current_user_id;
+ }
+
+ // Build cache key
$args = [
'status' => $request->get_param('status') ?? 'all',
'limit' => $request->get_param('limit') ?? 50,
- 'offset' => $request->get_param('offset') ?? 0
+ 'offset' => $request->get_param('offset') ?? 0,
+ 'date_start' => $request->get_param('date_start'),
+ 'date_end' => $request->get_param('date_end'),
];
+ $cache_key = "user_{$user_id}_" . $this->cache->generateKey($args);
+ // Check 304 Not Modified
+ $cache_check = $this->checkHeaders($request, $cache_key);
+ if ($cache_check instanceof WP_REST_Response) {
+ return $cache_check;
+ }
+
+ // Get referrals from manager
$referrals = JVB()->referrals()->getUserReferrals($user_id, $args);
- return new WP_REST_Response([
- 'success' => true,
- 'referrals' => $referrals
- ]);
+ $data = [
+ 'items' => $referrals,
+ 'total' => count($referrals)
+ ];
+
+ $response = $this->success($data);
+ return $this->addCacheHeaders($response);
}
/**
+ * POST /referrals
+ * Handle various referral actions based on 'action' parameter
+ */
+ public function handleAction(WP_REST_Request $request): WP_REST_Response
+ {
+ $action = $request->get_param('action');
+
+ return match($action) {
+ 'invite' => $this->actionInvite($request),
+ 'consulted' => $this->actionUpdateStatus($request, 'consulted'),
+ 'treated' => $this->actionUpdateStatus($request, 'treated'),
+ 'remove' => $this->actionRemove($request),
+ 'resend' => $this->actionResend($request),
+ default => $this->error('Invalid action', 'invalid_action', 400)
+ };
+ }
+
+ /**
+ * Action: Send batch referral invitations
+ */
+ protected function actionInvite(WP_REST_Request $request): WP_REST_Response
+ {
+ $user = absint($request->get_param('user'));
+ if (!$user || !get_userdata($user)) {
+ return $this->error('Invalid user', 'invalid_user', 400);
+ }
+
+ //Additional check to not send too many emails in an hour
+ $user = absint($request->get_param('user'));
+ $transient_key = "referral_invite_limit_{$user}";
+ $recent_invites = get_transient($transient_key) ?: 0;
+
+ if ($recent_invites >= 20) { // Max 5 batch invites per hour
+ return $this->error('Too many invitations sent. Please try again later.', 'rate_limit', 429);
+ }
+ set_transient($transient_key, $recent_invites + 1, HOUR_IN_SECONDS);
+
+ $subject = sanitize_text_field($request->get_param('subject'));
+ $message = sanitize_textarea_field($request->get_param('message'));
+ $invitations = $request->get_param('invite');
+
+ // Validate and sanitize invitations
+ $sanitized_invitations = [];
+ foreach ($invitations as $invite) {
+ if (isset($invite['name'], $invite['email'])) {
+ $sanitized_invitations[] = [
+ 'name' => sanitize_text_field($invite['name']),
+ 'email' => sanitize_email($invite['email'])
+ ];
+ }
+ }
+
+ if (empty($sanitized_invitations)) {
+ return $this->error('No valid invitations provided', 'no_invitations', 400);
+ }
+
+ $operationID = sanitize_text_field($request->get_param('id'));
+ JVB()->queue()->queueOperation(
+ 'referral_invite',
+ $user,
+ [
+ 'subject' => $subject,
+ 'message' => $message,
+ 'invitations' => $sanitized_invitations
+ ],
+ ['operation_id' => $operationID]
+ );
+
+ return $this->queued($operationID, 'Referral invitations queued');
+ }
+
+ /**
+ * Action: Update referral status (admin only)
+ */
+ protected function actionUpdateStatus(WP_REST_Request $request, string $status): WP_REST_Response
+ {
+ if (!current_user_can('manage_options')) {
+ return $this->forbidden('Admin permission required');
+ }
+
+ $referral_id = $request->get_param('referral_id');
+ if (!$referral_id) {
+ return $this->error('referral_id required', 'missing_id', 400);
+ }
+
+ // Get referral using CustomTable
+ $referral = $this->referrals->get(['id' => $referral_id]);
+ if (!$referral) {
+ return $this->notFound('Referral not found');
+ }
+
+ // Update status
+ $update_data = ['status' => $status];
+ $update_data["{$status}_at"] = current_time('mysql');
+
+ if ($status === 'treated') {
+ $update_data['treatment_count'] = ($referral->treatment_count ?? 0) + 1;
+ }
+
+ $updated = $this->referrals->update($update_data, ['id' => $referral_id]);
+
+ if ($updated !== false) {
+ // Create rewards if treated
+ if ($status === 'treated') {
+ $this->createRewards($referral);
+ }
+
+ $this->cache->flush();
+ return $this->success(['message' => "Referral marked as {$status}"]);
+ }
+
+ return $this->error('Failed to update referral', 'update_failed', 500);
+ }
+
+ /**
+ * Action: Remove referral
+ */
+ protected function actionRemove(WP_REST_Request $request): WP_REST_Response
+ {
+ $referral_id = $request->get_param('referral_id');
+ if (!$referral_id) {
+ return $this->error('referral_id required', 'missing_id', 400);
+ }
+
+ // Get referral
+ $referral = $this->referrals->get(['id' => $referral_id]);
+ if (!$referral) {
+ return $this->notFound('Referral not found');
+ }
+
+ // Check ownership
+ $current_user_id = get_current_user_id();
+ if ($referral->referrer_id != $current_user_id && !current_user_can('manage_options')) {
+ return $this->forbidden('Unauthorized');
+ }
+
+ // Can only remove pending referrals
+ if ($referral->status !== 'pending') {
+ return $this->error('Can only remove pending referrals', 'invalid_status', 400);
+ }
+
+ $this->referrals->delete(['id' => $referral_id]);
+ $this->cache->flush();
+
+ return $this->success(['message' => 'Referral removed']);
+ }
+
+ /**
+ * Action: Resend invitation
+ */
+ protected function actionResend(WP_REST_Request $request): WP_REST_Response
+ {
+ $referral_id = $request->get_param('referral_id');
+ if (!$referral_id) {
+ return $this->error('referral_id required', 'missing_id', 400);
+ }
+
+ $current_user_id = get_current_user_id();
+
+ // Get referral with ownership check
+ $referral = $this->referrals->where([
+ 'id' => $referral_id,
+ 'referrer_id' => $current_user_id
+ ])->first();
+
+ if (!$referral) {
+ return $this->notFound('Referral not found');
+ }
+
+ // Check rate limit (once per week)
+ $transient_key = 'referral_last_invite_' . md5($referral->referee_email);
+ $last_invite = get_transient($transient_key);
+
+ if ($last_invite && (time() - $last_invite) < WEEK_IN_SECONDS) {
+ return $this->error(
+ 'Can only resend once per week',
+ 'rate_limit',
+ 429
+ );
+ }
+
+ // Resend via referral manager
+ $result = JVB()->referrals()->sendReferralInvitation(
+ $current_user_id,
+ $referral->referee_email,
+ $referral->referee_name,
+ sprintf('Reminder: Join %s', get_bloginfo('name')),
+ 'Just a friendly reminder about my invitation!'
+ );
+
+ if (is_wp_error($result)) {
+ return $this->error($result->get_error_message(), 'send_failed', 500);
+ }
+
+ set_transient($transient_key, time(), WEEK_IN_SECONDS);
+ return $this->success(['message' => 'Invitation resent']);
+ }
+
+ /**
+ * GET /referrals/code
* Get user's referral code
*/
- public function getReferralCode(WP_REST_Request $request): WP_REST_Response
+ public function getCode(WP_REST_Request $request): WP_REST_Response
{
- $user_id = get_current_user_id();
+ $user_id = $request->get_param('user') ?? get_current_user_id();
+
+ // Check permission
+ if ($user_id != get_current_user_id() && !current_user_can('manage_options')) {
+ return $this->forbidden('Unauthorized');
+ }
+
$code = JVB()->referrals()->getUserReferralCode($user_id);
if (is_wp_error($code)) {
- return new WP_REST_Response([
- 'success' => false,
- 'message' => $code->get_error_message()
- ], 400);
+ return $this->error($code->get_error_message(), 'code_error', 400);
}
- return new WP_REST_Response([
- 'success' => true,
+ return $this->success([
'code' => $code,
'share_url' => home_url('/?ref=' . $code)
]);
}
/**
- * Update user's referral code
+ * POST /referrals/code
+ * Validate a referral code
*/
- public function updateReferralCode(WP_REST_Request $request): WP_REST_Response
- {
- $user_id = get_current_user_id();
- $new_code = strtoupper(sanitize_text_field($request->get_param('code')));
-
- $result = JVB()->referrals()->getUserReferralCode($user_id, $new_code);
-
- if (is_wp_error($result)) {
- return new WP_REST_Response([
- 'success' => false,
- 'message' => $result->get_error_message()
- ], 400);
- }
-
- return new WP_REST_Response([
- 'success' => true,
- 'code' => $result,
- 'message' => 'Referral code updated successfully'
- ]);
- }
-
- /**
- * Track referral click and store in session
- */
- public function trackReferralClick(WP_REST_Request $request): WP_REST_Response
+ public function validateCode(WP_REST_Request $request): WP_REST_Response
{
$code = strtoupper(sanitize_text_field($request->get_param('code')));
- // Start session if not already started
- if (session_status() === PHP_SESSION_NONE) {
- session_start();
+ if (empty($code)) {
+ return $this->error('Code required', 'missing_code', 400);
}
- // Store referral code in both session and cookie (30 day expiry)
- $_SESSION[BASE . 'referral_code'] = $code;
- setcookie(BASE . 'referral_code', $code, time() + (30 * DAY_IN_SECONDS), '/');
+ $referrer = JVB()->referrals()->getUserByReferralCode($code);
- return new WP_REST_Response([
- 'success' => true,
- 'message' => 'Referral tracked'
+ if (!$referrer) {
+ return $this->error('Invalid referral code', 'invalid_code', 404);
+ }
+
+ // Check self-referral
+ if (is_user_logged_in() && get_current_user_id() === $referrer->ID) {
+ return $this->error('Cannot use your own referral code', 'self_referral', 400);
+ }
+
+ return $this->success([
+ 'valid' => true,
+ 'code' => $code,
+ 'referrer_name' => $referrer->display_name
]);
}
/**
- * Mark referral as treated
+ * GET /referrals/stats
+ * Get user's referral statistics
*/
- public function markAsTreated(WP_REST_Request $request): WP_REST_Response
+ public function getStats(WP_REST_Request $request): WP_REST_Response
{
- $referral_id = intval($request->get_param('id'));
+ $user_id = $request->get_param('user') ?? get_current_user_id();
+ $cache_key = "stats_{$user_id}";
- $result = JVB()->referrals()->markAsTreated($referral_id, true);
-
- if (!$result) {
- return new WP_REST_Response([
- 'success' => false,
- 'message' => 'Failed to update referral'
- ], 400);
+ // Check 304 Not Modified
+ $cache_check = $this->checkHeaders($request, $cache_key);
+ if ($cache_check instanceof WP_REST_Response) {
+ return $cache_check;
}
- return new WP_REST_Response([
- 'success' => true,
- 'message' => 'Referral marked as treated and rewards created'
- ]);
- }
-
- /**
- * Get user stats
- */
- public function getUserStats(WP_REST_Request $request): WP_REST_Response
- {
- $user_id = get_current_user_id();
$stats = JVB()->referrals()->getUserStats($user_id);
- return new WP_REST_Response([
- 'success' => true,
- 'stats' => $stats
- ]);
+ $response = $this->success(['items' => [$stats]]);
+ return $this->addCacheHeaders($response);
}
/**
- * Get top referrers
- */
- public function getTopReferrers(WP_REST_Request $request): WP_REST_Response
- {
- $period = $request->get_param('period') ?? 'week';
- $limit = $request->get_param('limit') ?? 10;
-
- $top_referrers = JVB()->referrals()->getTopReferrers($limit, $period);
-
- return new WP_REST_Response([
- 'success' => true,
- 'period' => $period,
- 'referrers' => $top_referrers
- ]);
- }
-
- /**
- * Get referral settings
+ * GET /referrals/settings
*/
public function getSettings(WP_REST_Request $request): WP_REST_Response
{
- $settings = get_option(BASE . 'referral_settings', []);
-
- return new WP_REST_Response([
- 'success' => true,
- 'settings' => $settings
- ]);
+ $settings = JVB()->referrals()->getRewardSettings();
+ return $this->success(['settings' => $settings]);
}
/**
- * Update referral settings
+ * POST /referrals/settings
*/
public function updateSettings(WP_REST_Request $request): WP_REST_Response
{
$settings = [
- 'referrer_reward_type' => $request->get_param('referrer_reward_type') ?? 'per_user',
+ 'referrer_reward_type' => $request->get_param('referrer_reward_type') ?? 'fixed',
'referrer_reward_amount' => floatval($request->get_param('referrer_reward_amount') ?? 25),
+ 'referrer_reward_applies_to' => $request->get_param('referrer_reward_applies_to') ?? 'per_user',
'referee_reward_type' => $request->get_param('referee_reward_type') ?? 'percentage',
'referee_reward_amount' => floatval($request->get_param('referee_reward_amount') ?? 20),
'referee_reward_applies_to' => $request->get_param('referee_reward_applies_to') ?? 'first_order'
];
update_option(BASE . 'referral_settings', $settings);
+ $this->cache->flush();
- return new WP_REST_Response([
- 'success' => true,
- 'message' => 'Settings updated successfully',
+ return $this->success([
+ 'message' => 'Settings updated',
'settings' => $settings
]);
}
+
+ /**
+ * Helper: Get all referrals (admin only)
+ */
+ protected function getAllReferrals(WP_REST_Request $request): WP_REST_Response
+ {
+ global $wpdb;
+
+ $where = ['1=1'];
+ $where_params = [];
+
+ // Build WHERE conditions
+ $status = $request->get_param('status');
+ if ($status && $status !== 'all') {
+ $where[] = 'status = %s';
+ $where_params[] = $status;
+ }
+
+ if ($date_start = $request->get_param('date_start')) {
+ $where[] = 'referred_at >= %s';
+ $where_params[] = $date_start;
+ }
+
+ if ($date_end = $request->get_param('date_end')) {
+ $where[] = 'referred_at <= %s';
+ $where_params[] = $date_end;
+ }
+
+ $search = $request->get_param('search');
+ if (!empty($search)) {
+ $search_term = '%' . $wpdb->esc_like($search) . '%';
+ $where[] = '(r.referee_name LIKE %s OR r.referee_email LIKE %s OR r.referral_code LIKE %s OR u.display_name LIKE %s)';
+ $where_params[] = $search_term;
+ $where_params[] = $search_term;
+ $where_params[] = $search_term;
+ $where_params[] = $search_term;
+ }
+
+ $limit = $request->get_param('limit') ?? 50;
+ $offset = $request->get_param('offset') ?? 0;
+
+ $where_params[] = $limit;
+ $where_params[] = $offset;
+
+ // Use CustomTable's query method
+ $query = "SELECT r.*, u.display_name as referrer_name
+ FROM {table} r
+ LEFT JOIN {$wpdb->users} u ON r.referrer_id = u.ID
+ WHERE " . implode(' AND ', $where) . "
+ ORDER BY referred_at DESC
+ LIMIT %d OFFSET %d";
+
+ $items = $this->referrals->queryResults($query, $where_params);
+
+ return $this->success([
+ 'items' => $items,
+ 'total' => count($items)
+ ]);
+ }
+
+ /**
+ * Helper: Create rewards for completed referral
+ */
+ protected function createRewards(object $referral): void
+ {
+ $settings = JVB()->referrals()->getRewardSettings();
+
+ // Referrer reward
+ $this->rewards->insert([
+ 'referral_id' => $referral->id,
+ 'user_id' => $referral->referrer_id,
+ 'reward_type' => 'referrer',
+ 'amount' => $settings['referrer_reward_amount'],
+ 'reward_calculation' => $settings['referrer_reward_type'],
+ 'status' => 'available'
+ ]);
+
+ // Referee reward
+ if ($referral->referee_id) {
+ $this->rewards->insert([
+ 'referral_id' => $referral->id,
+ 'user_id' => $referral->referee_id,
+ 'reward_type' => 'referee',
+ 'amount' => $settings['referee_reward_amount'],
+ 'reward_calculation' => $settings['referee_reward_type'],
+ 'status' => 'available'
+ ]);
+ }
+ }
+
+ /**
+ * Process queued referral operations
+ */
+ public function processOperation(WP_Error|array $result, object $operation, array $data): array|WP_Error
+ {
+ if ($operation->type !== 'referral_invite') {
+ return $result;
+ }
+
+ $result = JVB()->referrals()->sendBatchReferralInvitations(
+ $operation->user_id,
+ $data['invitations'],
+ $data['subject'],
+ $data['message']
+ );
+
+ if ($result['success']) {
+ $this->cache->flush();
+ }
+
+ return [
+ 'success' => true,
+ 'message' => sprintf(
+ 'Sent invitations. Success: %d. Failed: %d.',
+ count($result['result']['success']),
+ count($result['result']['failed'])
+ ),
+ 'details' => [
+ 'successful' => $result['result']['success'],
+ 'failed' => $result['result']['failed'],
+ 'total' => count($data['invitations'])
+ ]
+ ];
+ }
+
+ /**
+ * Handle client CSV upload
+ */
+ public function handleClientUpload(WP_REST_Request $request): WP_REST_Response
+ {
+ if (empty($_FILES['file'])) {
+ return $this->error('No file uploaded', 'no_file', 400);
+ }
+
+ $file = $_FILES['file'];
+
+ if ($file['error'] !== UPLOAD_ERR_OK) {
+ return $this->error('File upload error: ' . $file['error'], 'upload_error', 400);
+ }
+
+ // Validate file type
+ $allowed_types = ['text/csv', 'application/vnd.ms-excel', 'text/plain'];
+ $finfo = finfo_open(FILEINFO_MIME_TYPE);
+ $mime_type = finfo_file($finfo, $file['tmp_name']);
+ finfo_close($finfo);
+
+ if (!in_array($mime_type, $allowed_types) && !in_array($file['type'], $allowed_types)) {
+ return $this->error('File must be a CSV', 'invalid_file_type', 400);
+ }
+
+ // Validate file size (10MB max)
+ if ($file['size'] > 10 * 1024 * 1024) {
+ return $this->error('File size exceeds 10MB limit', 'file_too_large', 400);
+ }
+
+ // Import using JaneAppClientImporter
+ $importer = new JaneAppClientImporter();
+ $default_role = get_option(BASE . 'referral_role', Site::getDefaultReferralRole());
+
+ $options = [
+ 'update_existing' => true,
+ 'send_welcome_email' => false,
+ 'create_users' => true,
+ 'default_role' => $default_role
+ ];
+
+ $result = $importer->importFromCSV($file['tmp_name'], $options);
+
+ if (is_wp_error($result)) {
+ return $this->error($result->get_error_message(), 'import_failed', 500);
+ }
+
+ $this->cache->flush();
+
+ return $this->success([
+ 'message' => sprintf(
+ 'Import complete: %d created, %d updated, %d skipped',
+ $result['created'],
+ $result['updated'],
+ $result['skipped']
+ ),
+ 'stats' => $result,
+ 'skipped_details' => $result['skipped_details'] ?? []
+ ]);
+ }
+
+ /**
+ * Handle sales CSV upload
+ */
+ public function handleSalesUpload(WP_REST_Request $request): WP_REST_Response
+ {
+ if (empty($_FILES['file'])) {
+ return $this->error('No file uploaded', 'no_file', 400);
+ }
+
+ $file = $_FILES['file'];
+
+ if ($file['error'] !== UPLOAD_ERR_OK) {
+ return $this->error('File upload error: ' . $file['error'], 'upload_error', 400);
+ }
+
+ // Validate file type
+ $allowed_types = ['text/csv', 'application/vnd.ms-excel', 'text/plain'];
+ $finfo = finfo_open(FILEINFO_MIME_TYPE);
+ $mime_type = finfo_file($finfo, $file['tmp_name']);
+ finfo_close($finfo);
+
+ if (!in_array($mime_type, $allowed_types) && !in_array($file['type'], $allowed_types)) {
+ return $this->error('File must be a CSV', 'invalid_file_type', 400);
+ }
+
+ // Validate file size (10MB max)
+ if ($file['size'] > 10 * 1024 * 1024) {
+ return $this->error('File size exceeds 10MB limit', 'file_too_large', 400);
+ }
+
+ // Import using JaneAppSalesImporter
+ $importer = new JaneAppSalesImporter();
+ $result = $importer->importFromCSV($file['tmp_name'], ['skip_existing' => true]);
+
+ if (is_wp_error($result)) {
+ return $this->error($result->get_error_message(), 'import_failed', 500);
+ }
+
+ $this->cache->flush();
+
+ return $this->success([
+ 'message' => 'Sales imported successfully',
+ 'stats' => $result
+ ]);
+ }
}
--
Gitblit v1.10.0