From 747d741293e064a979d7bf6c143ef969ea6d7629 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 24 May 2026 20:49:44 +0000
Subject: [PATCH] =GMBReview block minor tweaks. Refactored ReferralManager.php and ReferralRoutes.php to utilize the manager for all logic, and CustomTable for table interactions.
---
inc/managers/ReferralManager.php | 703 ++++++++++++++++++++++++++++++++-------------------------
1 files changed, 394 insertions(+), 309 deletions(-)
diff --git a/inc/managers/ReferralManager.php b/inc/managers/ReferralManager.php
index fe97fea..10ffbaf 100644
--- a/inc/managers/ReferralManager.php
+++ b/inc/managers/ReferralManager.php
@@ -20,7 +20,6 @@
*/
class ReferralManager
{
- protected $wpdb;
protected MagicLinkManager $magic_link;
protected Cache $cache;
protected Cache $requestCache;
@@ -37,12 +36,12 @@
// Default reward settings
protected array $default_settings = [
- 'referrer_reward_applies_to' => 'per_user', // 'per_user' or 'flat_total'
- 'referrer_reward_amount' => 25.00,
- 'referrer_reward_type' => 'fixed',
- 'referee_reward_type' => 'percentage', // 'percentage' or 'fixed'
- 'referee_reward_amount' => 20, // 20% or $20
- 'referee_reward_applies_to' => 'first_order', // 'first_order' or 'all_orders'
+ 'from_user_reward_applies_to' => 'per_user', // 'per_user' or 'flat_total'
+ 'from_user_reward_amount' => 25.00,
+ 'from_user_reward_type' => 'fixed',
+ 'to_user_reward_type' => 'percentage', // 'percentage' or 'fixed'
+ 'to_user_reward_amount' => 20, // 20% or $20
+ 'to_user_reward_applies_to' => 'first_order', // 'first_order' or 'all_orders'
];
protected string $role;
@@ -54,8 +53,7 @@
$this->defineTables();
$this->role = Site::getDefaultReferralRole();
$this->default_settings['referral_role'] = $this->role;
- global $wpdb;
- $this->wpdb = $wpdb;
+
$this->cache = Cache::for('referrals', WEEK_IN_SECONDS);
$this->requestCache = Cache::for('referral_requests', WEEK_IN_SECONDS)->connect('referrals', true);
$this->statsCache = Cache::for('referral_stats', WEEK_IN_SECONDS)->connect('referrals', true);
@@ -65,9 +63,6 @@
$this->statsCache->flush();
}
- $this->referrals_table = $wpdb->prefix . BASE . 'referrals';
- $this->rewards_table = $wpdb->prefix . BASE . 'referral_rewards';
-
$this->referralPage = $this->getReferralPageId();
$this->settings = $this->getRewardSettings();
@@ -220,7 +215,7 @@
'id' => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
'referral_id' => 'bigint(20) unsigned NOT NULL',
'user_id' => "{$table->getUserIDType()} NOT NULL",
- 'reward_type' => "ENUM('referrer', 'referee') NOT NULL",
+ 'reward_type' => "ENUM('from_user', 'to_user') NOT NULL",
'amount' => 'decimal(10,2) NOT NULL',
'reward_calculation'=> "ENUM('percentage', 'fixed')",
'status' => "ENUM('available', 'redeemed', 'expired', 'cancelled') DEFAULT 'available'",
@@ -521,18 +516,24 @@
return false; // No referral code - regular registration
}
- // Find the referrer
- $referrer = $this->codes->pluck('user_id', ['code' => $referral['referral_code']]);
- if (empty($referrer)) {
+ // Find the from_user
+ $from_user = $this->codes->pluck('user_id', ['code' => $referral['referral_code']]);
+ if (empty($from_user)) {
//This should not happen, but whatever
return false;
}
- $referrer = $referrer[0];
+
+ // Check if this email already has a referral record
+ if ($this->isEmailInvited($referral['to_email'])) {
+ return false;
+ }
+
+ $from_user = $from_user[0];
$record = $this->referrals->findOrCreate([
'to_user' => $user_id,
'referral_code' => $referral['referral_code'],
], [
- 'from_user' => $referrer,
+ 'from_user' => $from_user,
'to_email' => $referral['to_email'],
'to_name' => $userData['first_name'],
// 'to_phone' =>
@@ -558,39 +559,38 @@
$this->cache->flush();
// Fire action for tracking
- do_action('jvb_referral_processed', $user_id, $referrer->ID, $referral['referral_code']);
+ do_action('jvb_referral_processed', $user_id, $from_user->ID, $referral['referral_code']);
- // Send notification to referrer
- $this->sendReferrerNotification($referrer->ID, $userData['display_name']);
+ // Send notification to from_user
+ $this->sendReferrerNotification($from_user->ID, $userData['display_name']);
return true;
}
/**
* Create a referral record in the database
*
- * @param int $referrer_id
- * @param int $referee_id
+ * @param int $from_user_id
+ * @param int $to_user_id
* @param string $code
* @return int|false
*/
- public function createReferral(int $referrer_id, int $referee_id, string $code)
+ public function createReferral(int $from_user_id, int $to_user_id, string $code)
{
- $user = get_user_by('ID', $referee_id);
+ $user = get_user_by('ID', $to_user_id);
- return $this->wpdb->insert(
- $this->referrals_table,
+ return $this->referrals->findOrCreate([
[
- 'referrer_id' => $referrer_id,
- 'referee_id' => $referee_id,
- 'referee_name' => $user->display_name,
- 'referee_email' => $user->user_email,
- 'referee_phone' => get_user_meta($referee_id, BASE . 'phone', true) ?: '',
+ 'to_user' => $to_user_id,
+ ],
+ [
+ 'from_user' => $from_user_id,
+ 'to_name' => $user->display_name,
+ 'to_email' => $user->user_email,
+ 'to_phone' => get_user_meta($to_user_id, BASE . 'phone', true) ?: '',
'referral_code' => $code,
'status' => 'pending',
- 'referred_at' => current_time('mysql')
- ],
- ['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s']
- );
+ ]
+ ]);
}
/**
@@ -615,17 +615,16 @@
}
/**
- * Get referral record by referee ID
+ * Get referral record by to_user ID
*
- * @param int $referee_id
+ * @param int $to_user_id
* @return object|null
*/
- public function getReferralByReferee(int $referee_id): ?object
+ public function getReferralByReferee(int $to_user_id): ?object
{
- $result = $this->wpdb->get_row($this->wpdb->prepare(
- "SELECT * FROM {$this->referrals_table} WHERE referee_id = %d",
- $referee_id
- ));
+ $result = $this->referrals->get([
+ 'to_user' => $to_user_id
+ ]);
return $result ?: null;
}
@@ -641,15 +640,14 @@
{
$status = $treated ? 'treated' : 'pending';
- $result = $this->wpdb->update(
- $this->referrals_table,
+ $result = $this->referrals->update(
[
- 'status' => $status,
+ 'status' => $status,
'treated_at' => $treated ? current_time('mysql') : null
],
- ['id' => $referral_id],
- ['%s', '%s'],
- ['%d']
+ [
+ 'id' => $referral_id
+ ]
);
if ($result && $treated) {
@@ -661,53 +659,41 @@
}
/**
- * Create reward records for both referrer and referee
+ * Create reward records for both from_user and to_user
*
* @param int $referral_id
*/
protected function createRewardRecords(int $referral_id): void
{
- $referral = $this->wpdb->get_row($this->wpdb->prepare(
- "SELECT * FROM {$this->referrals_table} WHERE id = %d",
- $referral_id
- ));
+ $referral = $this->referrals->get(['id' => $referral_id]);
if (!$referral) {
return;
}
- // Create referrer reward
- $this->wpdb->insert(
- $this->rewards_table,
- [
- 'referral_id' => $referral_id,
- 'user_id' => $referral->referrer_id,
- 'reward_type' => 'referrer',
- 'amount' => $this->settings['referrer_reward_amount'],
- 'status' => 'available',
- 'created_at' => current_time('mysql')
- ],
- ['%d', '%d', '%s', '%f', '%s', '%s']
- );
+ // Create from_user reward
+ $fromUserReward = $this->rewards->insert([
+ 'referral_id' => $referral_id,
+ 'user_id' => $referral->from_user,
+ 'reward_type' => 'from_user',
+ 'amount' => $this->settings['from_user_reward_amount'],
+ 'reward_calculation' => $this->settings['from_user_reward_type']
+ ]);
- // Create referee reward
- $referee_amount = $this->settings['referee_reward_type'] === 'percentage'
- ? $this->settings['referee_reward_amount'] // Store as percentage
- : $this->settings['referee_reward_amount']; // Store as fixed amount
- $this->wpdb->insert(
- $this->rewards_table,
- [
- 'referral_id' => $referral_id,
- 'user_id' => $referral->referee_id,
- 'reward_type' => 'referee',
- 'amount' => $referee_amount,
- 'reward_calculation' => $this->settings['referee_reward_type'],
- 'status' => 'available',
- 'created_at' => current_time('mysql')
- ],
- ['%d', '%d', '%s', '%f', '%s', '%s', '%s']
- );
+ // Create to_user reward
+ $to_user_amount = $this->settings['to_user_reward_type'] === 'percentage'
+ ? $this->settings['to_user_reward_amount'] // Store as percentage
+ : $this->settings['to_user_reward_amount']; // Store as fixed amount
+
+ $toUserReward = $this->rewards->insert([
+ 'referral_id' => $referral_id,
+ 'user_id' => $referral->to_user,
+ 'reward_type' => 'to_user',
+ 'amount' => $to_user_amount,
+ 'reward_calculation' => $this->settings['to_user_reward_type']
+ ]);
+
}
/**
@@ -723,47 +709,43 @@
'status' => 'all',
'limit' => 100,
'offset' => 0,
- 'orderby' => 'referred_at',
+ 'orderby' => 'created_at',
'order' => 'DESC'
];
$args = wp_parse_args($args, $defaults);
+ $args['status'] = strtolower($args['status']);
+ $tableArgs = [
+ 'where' => [
+ 'from_user' => $user_id,
+ ],
+ 'limit' => $args['limit'],
+ 'offset' => $args['offset'],
+ 'orderby' => $args['orderby'],
+ 'order' => $args['order']
+ ];
+ if (in_array($args['status'], ['pending', 'consulted', 'treated', 'cancelled'])){
+ $tableArgs['status'] = $args['status'];
+ }
+ $referrals = $this->referrals->getMany($tableArgs);
- return $this->requestCache->remember(
- $this->requestCache->generateKey(array_merge(['user'=>$user_id], $args)),
- function() use ($user_id, $args) {
- $where = $this->wpdb->prepare("WHERE referrer_id = %d", $user_id);
-
- if ($args['status'] !== 'all') {
- $where .= $this->wpdb->prepare(" AND status = %s", $args['status']);
- }
-
- $query = "SELECT * FROM {$this->referrals_table}
- {$where}
- ORDER BY {$args['orderby']} {$args['order']}
- LIMIT {$args['limit']} OFFSET {$args['offset']}";
-
- $results = $this->wpdb->get_results($query);
-
- return array_map(function($referral) {
- $last_invite = get_transient('referral_last_invite_' . md5($referral->referee_email));
- $can_resend = !$last_invite || (time() - $last_invite) > WEEK_IN_SECONDS;
- $status = match($referral->status) {
- 'consulted' => 'Awaiting Treatment',
- 'treated' => 'Rewarded!',
- default => 'Pending',
- };
- return [
- 'id' => $referral->id,
- 'referee_name' => $referral->referee_name,
- 'referee_email' => $referral->referee_email,
- 'referred_at' => JVB()->routes('referral')->formatTimestamp($referral->referred_at),
- 'referral_status'=> $status,
- 'can_resend' => $can_resend
- ];
- }, $results);
- }
- );
+ return array_map(function($referral) {
+ $last_invite = get_transient('referral_last_invite_' . md5($referral->to_email));
+ $can_resend = !$last_invite || (time() - $last_invite) > WEEK_IN_SECONDS;
+ $status = match($referral->status) {
+ 'consulted' => 'Awaiting Treatment',
+ 'treated' => 'Rewarded!',
+ default => 'Pending',
+ };
+ return [
+ 'id' => $referral->id,
+ 'to_name' => $referral->to_name,
+ 'to_email' => $referral->to_email,
+ 'referred_at' => JVB()->routes('referral')->formatTimestamp($referral->referred_at),
+ 'referral_status'=> $status,
+ 'can_resend' => $can_resend
+ ];
+ }, $referrals);
}
@@ -778,24 +760,23 @@
return $this->statsCache->remember(
$user_id,
function() use ($user_id) {
- $stats = $this->wpdb->get_row($this->wpdb->prepare(
+ $stats = $this->referrals->queryResults(
"SELECT
- COUNT(*) as code_used,
- SUM(CASE WHEN status IN ('consulted', 'treated') THEN 1 ELSE 0 END) as consultations,
- SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treatments,
- SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending
- FROM {$this->referrals_table}
- WHERE referrer_id = %d",
- $user_id
- ), ARRAY_A);
+ COUNT(*) as code_used,
+ SUM(CASE WHEN status IN ('consulted', 'treated') THEN 1 ELSE 0 END) as consultations,
+ SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treatments,
+ SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending
+ FROM {table}
+ WHERE from_user = %d",
+ [$user_id],
+ ARRAY_A
+ );
+ $stats = $stats[0] ?? [];
- // Get total rewards earned (available + redeemed)
- $rewards = $this->wpdb->get_var($this->wpdb->prepare(
- "SELECT SUM(amount)
- FROM {$this->rewards_table}
- WHERE user_id = %d AND reward_type = 'referrer'",
- $user_id
- ));
+ $rewards = $this->rewards->queryVar(
+ "SELECT SUM(amount) FROM {table} WHERE user_id = %d AND reward_type = 'from_user'",
+ [$user_id]
+ );
$stats['total_rewards'] = floatval($rewards ?? 0);
$stats['user_id'] = $user_id;
@@ -805,7 +786,7 @@
}
/**
- * Get top referrers for a time period
+ * Get top from_users for a time period
*
* @param int $limit
* @param string $period 'day'|'week'|'month'|'all'
@@ -814,44 +795,40 @@
public function getTopReferrers(int $limit = 10, string $period = 'all'): array
{
return $this->statsCache->remember(
- $this->statsCache->generateKey(['limit'=>$limit, 'period' => $period]),
+ $this->statsCache->generateKey(['limit' => $limit, 'period' => $period]),
function() use ($limit, $period) {
$where = '';
-
if ($period !== 'all') {
- $date_where = match($period) {
- 'day' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
- 'week' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)",
- 'month' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
+ $date_clause = match($period) {
+ 'day' => "created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
+ 'week' => "created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)",
+ 'month' => "created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
default => "1=1"
};
-
- $where = "WHERE {$date_where}";
+ $where = "WHERE {$date_clause}";
}
- $query = "SELECT
- referrer_id,
+ $results = $this->referrals->queryResults(
+ "SELECT
+ from_user,
COUNT(*) as referral_count,
SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treated_count
- FROM {$this->referrals_table}
- {$where}
- GROUP BY referrer_id
- ORDER BY referral_count DESC
- LIMIT {$limit}";
+ FROM {table}
+ {$where}
+ GROUP BY from_user
+ ORDER BY referral_count DESC
+ LIMIT {$limit}"
+ );
- $results = $this->wpdb->get_results($query);
-
- // Enrich with user data
foreach ($results as &$result) {
- $user = get_user_by('ID', $result->referrer_id);
- $result->user_name = $user ? $user->display_name : 'Unknown';
- $result->user_email = $user ? $user->user_email : '';
+ $user = get_user_by('ID', $result->from_user);
+ $result->user_name = $user ? $user->display_name : 'Unknown';
+ $result->user_email = $user ? $user->user_email : '';
}
return $results;
}
);
-
}
/**
@@ -861,14 +838,14 @@
{
$yesterday = date('Y-m-d', strtotime('-1 day'));
- $new_referrals = $this->wpdb->get_results($this->wpdb->prepare(
- "SELECT r.*, u.display_name as referrer_name
- FROM {$this->referrals_table} r
- JOIN {$this->wpdb->users} u ON r.referrer_id = u.ID
- WHERE DATE(r.referred_at) = %s
- ORDER BY r.referred_at DESC",
- $yesterday
- ));
+ $new_referrals = $this->referrals->queryResults(
+ "SELECT {table}.*, u.display_name as from_user_name
+ FROM {table}
+ JOIN {$this->referrals->getUserTable()} u ON {table}.from_user = u.ID
+ WHERE DATE({table}.created_at) = %s
+ ORDER BY {table}.created_at DESC",
+ [$yesterday]
+ );
if (empty($new_referrals)) {
return;
@@ -888,13 +865,13 @@
foreach ($new_referrals as $ref) {
$cardContent = sprintf(
'<p><strong>%s</strong> (%s)</p>',
- esc_html($ref->referee_name),
- esc_html($ref->referee_email)
+ esc_html($ref->to_name),
+ esc_html($ref->to_email)
);
$cardContent .= sprintf(
'<p style="font-size:13px;color:%s;">Referred by: %s | Code: %s</p>',
JVB()->email()->colours['dark-200'],
- esc_html($ref->referrer_name),
+ esc_html($ref->from_name),
JVB()->email()->badge($ref->referral_code, 'info')
);
@@ -913,14 +890,13 @@
}
/**
- * Send weekly report with top referrers
+ * Send weekly report with top from_users
*/
public function sendWeeklyReport(): void
{
- $top_referrers = $this->getTopReferrers(10, 'week');
- $total_referrals = $this->wpdb->get_var(
- "SELECT COUNT(*) FROM {$this->referrals_table}
- WHERE referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)"
+ $top_from_users = $this->getTopReferrers(10, 'week');
+ $total_referrals = $this->referrals->queryVar(
+ "SELECT COUNT(*) FROM {table} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)"
);
if ($total_referrals == 0) {
@@ -939,7 +915,7 @@
// Leaderboard style
$rank = 1;
- foreach ($top_referrers as $referrer) {
+ foreach ($top_from_users as $from_user) {
$rankBadge = $rank <= 3
? JVB()->email()->badge('#' . $rank, $rank === 1 ? 'success' : 'info')
: '<span style="font-weight:600;color:' . JVB()->email()->colours['dark-200'] . ';">#' . $rank . '</span>';
@@ -947,12 +923,12 @@
$cardContent = sprintf(
'<p>%s <strong>%s</strong></p>',
$rankBadge,
- esc_html($referrer->user_name)
+ esc_html($from_user->user_name)
);
$stats = [
- JVB()->email()->stat($referrer->referral_count, 'Total Referrals'),
- JVB()->email()->stat($referrer->treated_count, 'Treated')
+ JVB()->email()->stat($from_user->referral_count, 'Total Referrals'),
+ JVB()->email()->stat($from_user->treated_count, 'Treated')
];
$cardContent .= JVB()->email()->grid($stats, 2);
@@ -983,10 +959,10 @@
foreach ($referrals as $referral) {
$csv .= sprintf(
'"%s","%s","%s","%s","%s","%s","%s","%s"' . "\n",
- $referral->referrer_name ?? 'Unknown',
- $referral->referee_name,
- $referral->referee_email,
- $referral->referee_phone,
+ $referral->from_name ?? 'Unknown',
+ $referral->to_name,
+ $referral->to_email,
+ $referral->to_phone,
$referral->referral_code,
$referral->status,
$referral->referred_at,
@@ -1003,11 +979,11 @@
/**
* Generate HTML email for weekly report
*
- * @param array $top_referrers
+ * @param array $top_from_users
* @param int $total_referrals
* @return string
*/
- protected function generateWeeklyReportEmail(array $top_referrers, int $total_referrals): string
+ protected function generateWeeklyReportEmail(array $top_from_users, int $total_referrals): string
{
$content = sprintf(
'<p>This week you had <strong>%d total referral%s</strong>.</p>',
@@ -1015,20 +991,20 @@
$total_referrals !== 1 ? 's' : ''
);
- $referrers = [];
+ $from_users = [];
$rank = 1;
- foreach ($top_referrers as $referrer) {
- $referrers[] = [
- 'label' => '#' . $rank++ . ' - ' . esc_html($referrer->user_name),
+ foreach ($top_from_users as $from_user) {
+ $from_users[] = [
+ 'label' => '#' . $rank++ . ' - ' . esc_html($from_user->user_name),
'value' => sprintf(
'<strong>Total Referrals:</strong> %d | <strong>Treated:</strong> %d',
- $referrer->referral_count,
- $referrer->treated_count
+ $from_user->referral_count,
+ $from_user->treated_count
)
];
}
- $content .= JVB()->email()->table($referrers, 'Top 10 Referrers This Week');
+ $content .= JVB()->email()->table($from_users, 'Top 10 Referrers This Week');
return $content;
}
@@ -1119,8 +1095,8 @@
<tbody>
<?php foreach ($referrals as $referral): ?>
<tr>
- <td><?php echo esc_html($referral->referee_name); ?></td>
- <td><?php echo esc_html($referral->referee_email); ?></td>
+ <td><?php echo esc_html($referral->to_name); ?></td>
+ <td><?php echo esc_html($referral->to_email); ?></td>
<td><?php echo esc_html(ucfirst($referral->status)); ?></td>
<td><?php echo esc_html($referral->referred_at); ?></td>
<td>
@@ -1250,17 +1226,17 @@
// Pre-fill code if from referral link
$prefill_code = $_GET['ref'] ?? '';
- $referrer_name = '';
+ $from_user_name = '';
if ($prefill_code) {
- $referrer = $this->getUserByReferralCode($prefill_code);
- $referrer_name = $referrer ? strtok($referrer->display_name, ' ') : '';
+ $from_user = $this->getUserByReferralCode($prefill_code);
+ $from_user_name = $from_user ? strtok($from_user->display_name, ' ') : '';
}
$header = sprintf(
'<header><h2>%sGet %s.</h2></header><h3>Have a code?</h3>%s<p>Enter your referral code to get started!</p>',
jvbIcon('confetti'),
esc_html($reward_text),
- ($referrer_name ? '<p>' . esc_html($referrer_name) . ' invited you to join us</p>' : '')
+ ($from_user_name ? '<p>' . esc_html($from_user_name) . ' invited you to join us</p>' : '')
);
$codeForm = sprintf(
@@ -1311,7 +1287,7 @@
'pattern' => '[A-Za-z0-9]+',
'maxLength' => 20,
'autocomplete'=>'off',
- 'data-referrer' => $referrer_name
+ 'data-from-user' => $from_user_name
]),
$turnstile,
jvbIcon('check-circle')
@@ -1375,14 +1351,14 @@
protected function getReferralSuccessMessage(string $code): string
{
- $referrer = $this->getUserByReferralCode($code);
+ $from_user = $this->getUserByReferralCode($code);
- if (!$referrer) {
+ if (!$from_user) {
return '';
}
- $reward_amount = $this->settings['referee_reward_amount'] ?? 20;
- $reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
+ $reward_amount = $this->settings['to_user_reward_amount'] ?? 20;
+ $reward_type = $this->settings['to_user_reward_type'] ?? 'percentage';
$reward_text = $reward_type === 'percentage'
? $reward_amount . '% off'
@@ -1411,7 +1387,7 @@
</a>
<div class="referred-by">
- Referred by <strong><?php echo esc_html($referrer->display_name); ?></strong>
+ Referred by <strong><?php echo esc_html($from_user->display_name); ?></strong>
</div>
</div>
<?php
@@ -1441,7 +1417,7 @@
<section class="copy">
<h4>Your Referral Link</h4>
- <div class="copy-group row btw nowrap">
+ <div class="copy-group row x-btw nowrap">
<code id="referral-link" class="copy-target"><?= esc_url($share_url) ?></code>
<button type="button" class="copy-btn" data-target="referral-link" title="Copy referral link">
<?= jvbIcon('copy'); ?>
@@ -1452,7 +1428,7 @@
<h4>Your Code</h4>
- <div class="copy-group row btw nowrap">
+ <div class="copy-group row x-btw nowrap">
<code id="referral-code" class="copy-target"><?= esc_html($referral_code) ?></code>
<button type="button" class="copy-btn" data-target="referral-code" title="Copy referral code">
<?= jvbIcon('copy'); ?>
@@ -1471,19 +1447,19 @@
</section>
<section class="stats-summary">
- <div class="row btw">
+ <div class="row x-btw">
<span class="stat-label">Total Referrals</span>
<span class="stat-value" data-stat="total">-</span>
</div>
- <div class="row btw">
+ <div class="row x-btw">
<span class="stat-label">Successful</span>
<span class="stat-value" data-stat="treated">-</span>
</div>
- <div class="row btw">
+ <div class="row x-btw">
<span class="stat-label">Pending</span>
<span class="stat-value" data-stat="pending">-</span>
</div>
- <div class="row btw highlight">
+ <div class="row x-btw highlight">
<span class="stat-label">Available Rewards</span>
<span class="stat-value" data-stat="rewards">$0.00</span>
</div>
@@ -1506,8 +1482,8 @@
return $content;
}
- $reward_amount = $this->settings['referee_reward_amount'] ?? 20;
- $reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
+ $reward_amount = $this->settings['to_user_reward_amount'] ?? 20;
+ $reward_type = $this->settings['to_user_reward_type'] ?? 'percentage';
$reward_text = $reward_type === 'percentage'
? $reward_amount . '% off'
@@ -1582,8 +1558,8 @@
return new WP_Error('user_exists', 'This person already has an account');
}
- // Get referrer info
- $referrer = get_user_by('ID', $user_id);
+ // Get from_user info
+ $from_user = get_user_by('ID', $user_id);
$referral_code = $this->getUserReferralCode($user_id);
if ($referral_code) {
@@ -1609,9 +1585,9 @@
], home_url('/'));
// Get reward text for email
- $reward_text = $this->settings['referee_reward_type'] === 'percentage'
- ? "{$this->settings['referee_reward_amount']}% off"
- : "\${$this->settings['referee_reward_amount']} off";
+ $reward_text = $this->settings['to_user_reward_type'] === 'percentage'
+ ? "{$this->settings['to_user_reward_amount']}% off"
+ : "\${$this->settings['to_user_reward_amount']} off";
// Build email content
$email_content =
@@ -1624,7 +1600,7 @@
<p>Click the button below to register and claim your reward:</p>
%s
<p><small>This invitation expires in 30 days.</small></p>',
- esc_html($referrer->display_name),
+ esc_html($from_user->display_name),
esc_html(get_bloginfo('name')),
nl2br(esc_html($message)),
esc_html($reward_text),
@@ -1743,10 +1719,7 @@
}
// Check if there's a pending referral for this email
- $existing = $this->wpdb->get_var($this->wpdb->prepare(
- "SELECT id FROM {$this->referrals_table} WHERE referee_email = %s",
- $email
- ));
+ $existing = $this->referrals->pluck('id', ['to_email' => $email]);
return !empty($existing);
}
@@ -1818,25 +1791,24 @@
*/
public function exportReferrals(string $start_date, string $end_date): string
{
- $referrals = $this->wpdb->get_results($this->wpdb->prepare(
+ $referrals = $this->referrals->queryResults(
"SELECT
- r.id,
- r.referee_name,
- r.referee_email,
- r.referee_phone,
- r.referral_code,
- r.referred_at,
- r.status,
- r.treated_at,
- u.display_name as referrer_name,
- u.user_email as referrer_email
- FROM {$this->referrals_table} r
- JOIN {$this->wpdb->users} u ON r.referrer_id = u.ID
- WHERE DATE(r.referred_at) BETWEEN %s AND %s
- ORDER BY r.referred_at DESC",
- $start_date,
- $end_date
- ));
+ {table}.id,
+ {table}.to_name,
+ {table}.to_email,
+ {table}.to_phone,
+ {table}.referral_code,
+ {table}.created_at,
+ {table}.status,
+ {table}.treated_at,
+ u.display_name as from_name,
+ u.user_email as from_email
+ FROM {table}
+ JOIN {$this->referrals->getUserTable()} u ON {table}.from_user = u.ID
+ WHERE DATE({table}.created_at) BETWEEN %s AND %s
+ ORDER BY {table}.created_at DESC",
+ [$start_date, $end_date]
+ );
// Build CSV
$csv_lines = [];
@@ -1859,15 +1831,15 @@
foreach ($referrals as $ref) {
$csv_lines[] = [
$ref->id,
- $ref->referee_name,
- $ref->referee_email,
- $ref->referee_phone ?: 'N/A',
+ $ref->to_name,
+ $ref->to_email,
+ $ref->to_phone ?: 'N/A',
$ref->referral_code,
$ref->referred_at,
ucfirst($ref->status),
$ref->treated_at ?: 'N/A',
- $ref->referrer_name,
- $ref->referrer_email
+ $ref->from_name,
+ $ref->from_email
];
}
@@ -1944,19 +1916,19 @@
public function sanitizeRewardSettings(array $settings): array
{
return [
- 'referrer_reward_applies_to' => in_array($settings['referrer_reward_applies_to'] ?? '', ['per_user', 'flat_total'])
- ? $settings['referrer_reward_applies_to']
+ 'from_user_reward_applies_to' => in_array($settings['from_user_reward_applies_to'] ?? '', ['per_user', 'flat_total'])
+ ? $settings['from_user_reward_applies_to']
: 'per_user',
- 'referrer_reward_amount' => floatval($settings['referrer_reward_amount'] ?? 25.00),
- 'referrer_reward_type' => in_array($settings['referrer_reward_type'] ?? '', ['fixed', 'percentage'])
- ? $settings['referrer_reward_type']
+ 'from_user_reward_amount' => floatval($settings['from_user_reward_amount'] ?? 25.00),
+ 'from_user_reward_type' => in_array($settings['from_user_reward_type'] ?? '', ['fixed', 'percentage'])
+ ? $settings['from_user_reward_type']
: 'fixed',
- 'referee_reward_type' => in_array($settings['referee_reward_type'] ?? '', ['percentage', 'fixed'])
- ? $settings['referee_reward_type']
+ 'to_user_reward_type' => in_array($settings['to_user_reward_type'] ?? '', ['percentage', 'fixed'])
+ ? $settings['to_user_reward_type']
: 'percentage',
- 'referee_reward_amount' => floatval($settings['referee_reward_amount'] ?? 20),
- 'referee_reward_applies_to' => in_array($settings['referee_reward_applies_to'] ?? '', ['first_order', 'all_orders'])
- ? $settings['referee_reward_applies_to']
+ 'to_user_reward_amount' => floatval($settings['to_user_reward_amount'] ?? 20),
+ 'to_user_reward_applies_to' => in_array($settings['to_user_reward_applies_to'] ?? '', ['first_order', 'all_orders'])
+ ? $settings['to_user_reward_applies_to']
: 'first_order',
];
}
@@ -2246,12 +2218,12 @@
} else {
data.items.forEach(function(ref) {
html += '<tr>';
- html += '<td>' + (ref.referrer_name || 'Unknown') + '</td>';
- html += '<td>' + (ref.referee_display_name || ref.referee_name) + '</td>';
- html += '<td>' + (ref.referee_display_email || ref.referee_email) + '</td>';
+ html += '<td>' + (ref.from_name || 'Unknown') + '</td>';
+ html += '<td>' + (ref.to_user_display_name || ref.to_name) + '</td>';
+ html += '<td>' + (ref.to_user_display_email || ref.to_email) + '</td>';
html += '<td><span class="referral-status ' + ref.status + '">' + ref.status + '</span></td>';
html += '<td>' + new Date(ref.referred_at).toLocaleDateString() + '</td>';
- html += '<td>' + (ref.referrer_total_referrals || 0) + '</td>';
+ html += '<td>' + (ref.from_user_total_referrals || 0) + '</td>';
html += '<td class="referral-actions">';
if (ref.status === 'pending') {
@@ -2408,24 +2380,24 @@
</tr>
<tr>
<th scope="row">
- <label for="referrer_reward_type">Reward Type</label>
+ <label for="from_user_reward_type">Reward Type</label>
</th>
<td>
- <select name="referrer_reward_type" id="referrer_reward_type">
- <option value="fixed" <?php selected($this->settings['referrer_reward_type'], 'fixed'); ?>>Fixed Amount</option>
- <option value="percentage" <?php selected($this->settings['referrer_reward_type'], 'percentage'); ?>>Percentage</option>
+ <select name="from_user_reward_type" id="from_user_reward_type">
+ <option value="fixed" <?php selected($this->settings['from_user_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+ <option value="percentage" <?php selected($this->settings['from_user_reward_type'], 'percentage'); ?>>Percentage</option>
</select>
</td>
</tr>
<tr>
<th scope="row">
- <label for="referrer_reward_amount">Reward Amount</label>
+ <label for="from_user_reward_amount">Reward Amount</label>
</th>
<td>
<input type="number"
- name="referrer_reward_amount"
- id="referrer_reward_amount"
- value="<?= esc_attr($this->settings['referrer_reward_amount']) ?>"
+ name="from_user_reward_amount"
+ id="from_user_reward_amount"
+ value="<?= esc_attr($this->settings['from_user_reward_amount']) ?>"
step="0.01"
min="0">
<p class="description">Amount in dollars or percentage</p>
@@ -2433,12 +2405,12 @@
</tr>
<tr>
<th scope="row">
- <label for="referrer_reward_applies_to">Applies To</label>
+ <label for="from_user_reward_applies_to">Applies To</label>
</th>
<td>
- <select name="referrer_reward_applies_to" id="referrer_reward_applies_to">
- <option value="per_user" <?php selected($this->settings['referrer_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
- <option value="flat_total" <?php selected($this->settings['referrer_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
+ <select name="from_user_reward_applies_to" id="from_user_reward_applies_to">
+ <option value="per_user" <?php selected($this->settings['from_user_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
+ <option value="flat_total" <?php selected($this->settings['from_user_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
</select>
</td>
</tr>
@@ -2448,24 +2420,24 @@
</tr>
<tr>
<th scope="row">
- <label for="referee_reward_type">Reward Type</label>
+ <label for="to_user_reward_type">Reward Type</label>
</th>
<td>
- <select name="referee_reward_type" id="referee_reward_type">
- <option value="percentage" <?php selected($this->settings['referee_reward_type'], 'percentage'); ?>>Percentage</option>
- <option value="fixed" <?php selected($this->settings['referee_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+ <select name="to_user_reward_type" id="to_user_reward_type">
+ <option value="percentage" <?php selected($this->settings['to_user_reward_type'], 'percentage'); ?>>Percentage</option>
+ <option value="fixed" <?php selected($this->settings['to_user_reward_type'], 'fixed'); ?>>Fixed Amount</option>
</select>
</td>
</tr>
<tr>
<th scope="row">
- <label for="referee_reward_amount">Reward Amount</label>
+ <label for="to_user_reward_amount">Reward Amount</label>
</th>
<td>
<input type="number"
- name="referee_reward_amount"
- id="referee_reward_amount"
- value="<?= esc_attr($this->settings['referee_reward_amount']) ?>"
+ name="to_user_reward_amount"
+ id="to_user_reward_amount"
+ value="<?= esc_attr($this->settings['to_user_reward_amount']) ?>"
step="0.01"
min="0">
<p class="description">Amount in dollars or percentage</p>
@@ -2473,12 +2445,12 @@
</tr>
<tr>
<th scope="row">
- <label for="referee_reward_applies_to">Applies To</label>
+ <label for="to_user_reward_applies_to">Applies To</label>
</th>
<td>
- <select name="referee_reward_applies_to" id="referee_reward_applies_to">
- <option value="first_order" <?php selected($this->settings['referee_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
- <option value="all_orders" <?php selected($this->settings['referee_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
+ <select name="to_user_reward_applies_to" id="to_user_reward_applies_to">
+ <option value="first_order" <?php selected($this->settings['to_user_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
+ <option value="all_orders" <?php selected($this->settings['to_user_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
</select>
</td>
</tr>
@@ -2692,7 +2664,7 @@
<details open>
<summary>Your Code</summary>
<h3>Share Link</h3>
- <div class="row btw nowrap">
+ <div class="row x-btw nowrap">
<code id="referral-link" class="copy-target"><?= home_url('/?ref=' . $referral_code) ?></code>
<button type="button" class="copy-btn" data-target="referral-link" title="Copy referral link">
<?= jvbIcon('copy'); ?>
@@ -2700,7 +2672,7 @@
</button>
</div>
<h3>Share Code</h3>
- <div class="row btw nowrap">
+ <div class="row x-btw nowrap">
<code id="referral-code" class="copy-target"><?= esc_html($referral_code) ?></code>
<button type="button" class="copy-btn" data-target="referral-code" title="Copy referral code">
<?= jvbIcon('copy'); ?>
@@ -2798,11 +2770,11 @@
->content('referral', 'Referral', 'Referrals')
// ->initMeta('custom', 'referral')
->setFields([
- 'referee_name' => [
+ 'to_name' => [
'label' => 'Name',
'type' => 'text',
],
- 'referee_email' => [
+ 'to_email' => [
'label' => 'Email',
'type' => 'text',
],
@@ -2876,12 +2848,12 @@
// Save reward settings
$settings = [
- 'referrer_reward_type' => sanitize_text_field($post_data['referrer_reward_type'] ?? 'fixed'),
- 'referrer_reward_amount' => floatval($post_data['referrer_reward_amount'] ?? 25.00),
- 'referrer_reward_applies_to' => sanitize_text_field($post_data['referrer_reward_applies_to'] ?? 'per_user'),
- 'referee_reward_type' => sanitize_text_field($post_data['referee_reward_type'] ?? 'percentage'),
- 'referee_reward_amount' => floatval($post_data['referee_reward_amount'] ?? 20),
- 'referee_reward_applies_to' => sanitize_text_field($post_data['referee_reward_applies_to'] ?? 'first_order')
+ 'from_user_reward_type' => sanitize_text_field($post_data['from_user_reward_type'] ?? 'fixed'),
+ 'from_user_reward_amount' => floatval($post_data['from_user_reward_amount'] ?? 25.00),
+ 'from_user_reward_applies_to' => sanitize_text_field($post_data['from_user_reward_applies_to'] ?? 'per_user'),
+ 'to_user_reward_type' => sanitize_text_field($post_data['to_user_reward_type'] ?? 'percentage'),
+ 'to_user_reward_amount' => floatval($post_data['to_user_reward_amount'] ?? 20),
+ 'to_user_reward_applies_to' => sanitize_text_field($post_data['to_user_reward_applies_to'] ?? 'first_order')
];
update_option(BASE . 'referral_settings', $settings);
@@ -2900,15 +2872,15 @@
}
/**
- * Get formatted reward text for referee
+ * Get formatted reward text for to_user
*
* @param bool $full Include "off your first treatment" text
* @return string
*/
public function getRewardText(bool $full = true): string
{
- $reward_amount = $this->settings['referee_reward_amount'] ?? 20;
- $reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
+ $reward_amount = $this->settings['to_user_reward_amount'] ?? 20;
+ $reward_type = $this->settings['to_user_reward_type'] ?? 'percentage';
$reward_text = $reward_type === 'percentage'
? $reward_amount . '% off'
@@ -2973,28 +2945,28 @@
}
/**
- * Send notification to referrer when someone registers
+ * Send notification to from_user when someone registers
*
- * @param int $referrer_id
- * @param string $referee_name
+ * @param int $from_user_id
+ * @param string $to_name
*/
- protected function sendReferrerNotification(int $referrer_id, string $referee_name): void
+ protected function sendReferrerNotification(int $from_user_id, string $to_name): void
{
- $referrer = get_userdata($referrer_id);
- if (!$referrer) {
+ $from_user = get_userdata($from_user_id);
+ if (!$from_user) {
return;
}
- $subject = sprintf('%s signed up with your referral code!', $referee_name);
+ $subject = sprintf('%s signed up with your referral code!', $to_name);
$message = sprintf(
"Great news! %s just signed up using your referral code.\n\n" .
"View your referrals: %s",
- $referee_name,
+ $to_name,
home_url('/dash/referrals')
);
JVB()->email()->sendEmail(
- $referrer->user_email,
+ $from_user->user_email,
$subject,
$message
);
@@ -3021,9 +2993,9 @@
return '';
}
- // Get referrer name
- $referrer = get_userdata($referral->referrer_id);
- $referrer_first_name = $referrer ? strtok($referrer->display_name, ' ') : 'Your friend';
+ // Get from_user name
+ $from_user = get_userdata($referral->from_user);
+ $from_user_first_name = $from_user ? strtok($from_user->display_name, ' ') : 'Your friend';
// Get reward text
$reward_text = $this->getRewardText(); // Just "20% off" or "$25 off"
@@ -3035,7 +3007,7 @@
?>
<div class="welcome-banner referral-welcome">
<div class="banner-content">
- <h3><?= jvbIcon('confetti') ?>Welcome! <small><b><?= esc_html($referrer_first_name) ?></b> invited you to save <b><?= esc_html($reward_text) ?></b>!</small></h3>
+ <h3><?= jvbIcon('confetti') ?>Welcome! <small><b><?= esc_html($from_user_first_name) ?></b> invited you to save <b><?= esc_html($reward_text) ?></b>!</small></h3>
<p>But we're not done yet! Here's what happens next:</p>
<div class="callout">
<ol>
@@ -3064,5 +3036,118 @@
<?php
return ob_get_clean();
}
+
+ public function updateStatus(int $referral_id, string $status): bool|WP_Error
+ {
+ $referral = $this->referrals->get(['id' => $referral_id]);
+ if (!$referral) {
+ return new WP_Error('not_found', 'Referral not found');
+ }
+
+ $data = ['status' => $status, "{$status}_at" => current_time('mysql')];
+ if ($status === 'treated') {
+ $data['treatment_count'] = ($referral->treatment_count ?? 0) + 1;
+ }
+
+ $result = $this->referrals->update($data, ['id' => $referral_id]);
+ if ($result === false) {
+ return new WP_Error('update_failed', 'Failed to update referral status');
+ }
+
+ if ($status === 'treated') {
+ $this->createRewardRecords($referral_id);
+ }
+
+ $this->cache->flush();
+ return true;
+ }
+
+ public function removeReferral(int $referral_id, int $user_id): bool|WP_Error
+ {
+ $referral = $this->referrals->get(['id' => $referral_id]);
+ if (!$referral) {
+ return new WP_Error('not_found', 'Referral not found');
+ }
+
+ if ($referral->from_user != $user_id && !current_user_can('manage_options')) {
+ return new WP_Error('unauthorized', 'Unauthorized');
+ }
+
+ if ($referral->status !== 'pending') {
+ return new WP_Error('invalid_status', 'Can only remove pending referrals');
+ }
+
+ $this->referrals->delete(['id' => $referral_id]);
+ $this->cache->flush();
+ return true;
+ }
+
+ public function resendInvitation(int $referral_id, int $user_id): bool|WP_Error
+ {
+ $referral = $this->referrals->where(['id' => $referral_id, 'from_user' => $user_id])->first();
+ if (!$referral) {
+ return new WP_Error('not_found', 'Referral not found');
+ }
+
+ $transient_key = 'referral_last_invite_' . md5($referral->to_email);
+ if (get_transient($transient_key)) {
+ return new WP_Error('rate_limit', 'Can only resend once per week');
+ }
+
+ $result = $this->sendReferralInvitation(
+ $user_id,
+ $referral->to_email,
+ $referral->to_name,
+ sprintf('Reminder: Join %s', get_bloginfo('name')),
+ 'Just a friendly reminder about my invitation!'
+ );
+
+ if (is_wp_error($result)) {
+ return $result;
+ }
+
+ set_transient($transient_key, time(), WEEK_IN_SECONDS);
+ return true;
+ }
+
+ public function getAllReferrals(array $args = []): array
+ {
+ $conditions = ['1=1'];
+ $values = [];
+
+ if (!empty($args['status']) && $args['status'] !== 'all') {
+ $conditions[] = '{table}.status = %s';
+ $values[] = $args['status'];
+ }
+
+ if (!empty($args['date_start'])) {
+ $conditions[] = 'DATE({table}.created_at) >= %s';
+ $values[] = $args['date_start'];
+ }
+
+ if (!empty($args['date_end'])) {
+ $conditions[] = 'DATE({table}.created_at) <= %s';
+ $values[] = $args['date_end'];
+ }
+
+ if (!empty($args['search'])) {
+ global $wpdb;
+ $like = '%' . $wpdb->esc_like($args['search']) . '%';
+ $conditions[] = '({table}.to_name LIKE %s OR {table}.to_email LIKE %s OR {table}.referral_code LIKE %s OR u.display_name LIKE %s)';
+ array_push($values, $like, $like, $like, $like);
+ }
+
+ array_push($values, absint($args['limit'] ?? 50), absint($args['offset'] ?? 0));
+
+ return $this->referrals->queryResults(
+ "SELECT {table}.*, u.display_name as from_name
+ FROM {table}
+ LEFT JOIN {$this->referrals->getUserTable()} u ON {table}.from_user = u.ID
+ WHERE " . implode(' AND ', $conditions) . "
+ ORDER BY {table}.created_at DESC
+ LIMIT %d OFFSET %d",
+ $values
+ );
+ }
}
--
Gitblit v1.10.0