chore: batch supporting UI, settings schema, templates, and docs updates
This commit is contained in:
@@ -249,6 +249,9 @@ class ModuleSettingsController extends WP_REST_Controller {
|
||||
|
||||
// Type validation
|
||||
$type = $field['type'] ?? 'text';
|
||||
$min = isset($field['min']) ? $field['min'] : null;
|
||||
$max = isset($field['max']) ? $field['max'] : null;
|
||||
|
||||
switch ($type) {
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
@@ -256,9 +259,39 @@ class ModuleSettingsController extends WP_REST_Controller {
|
||||
case 'url':
|
||||
$validated[$key] = sanitize_text_field($value);
|
||||
break;
|
||||
|
||||
|
||||
case 'number':
|
||||
$validated[$key] = floatval($value);
|
||||
// Handle empty string as valid for numbers (if min allows 0)
|
||||
if ($value === '' || $value === null) {
|
||||
// Only allow empty if there's a default
|
||||
if (isset($field['default'])) {
|
||||
$validated[$key] = $field['default'];
|
||||
} elseif ($min !== null && $min === 0) {
|
||||
$validated[$key] = 0;
|
||||
} else {
|
||||
$errors[$key] = sprintf(
|
||||
__('%s cannot be empty', 'woonoow'),
|
||||
$field['label'] ?? $key
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$num_value = floatval($value);
|
||||
if ($min !== null && $num_value < $min) {
|
||||
$errors[$key] = sprintf(
|
||||
__('%s must be at least %s', 'woonoow'),
|
||||
$field['label'] ?? $key,
|
||||
$min
|
||||
);
|
||||
} elseif ($max !== null && $num_value > $max) {
|
||||
$errors[$key] = sprintf(
|
||||
__('%s must be at most %s', 'woonoow'),
|
||||
$field['label'] ?? $key,
|
||||
$max
|
||||
);
|
||||
} else {
|
||||
$validated[$key] = $num_value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'toggle':
|
||||
@@ -277,6 +310,21 @@ class ModuleSettingsController extends WP_REST_Controller {
|
||||
$validated[$key] = sanitize_text_field($value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'multiselect':
|
||||
// Validate array of values against allowed options
|
||||
if (!is_array($value)) {
|
||||
$value = [];
|
||||
}
|
||||
$allowed_options = $field['options'] ?? [];
|
||||
$valid_values = [];
|
||||
foreach ($value as $v) {
|
||||
if (isset($allowed_options[$v])) {
|
||||
$valid_values[] = sanitize_text_field($v);
|
||||
}
|
||||
}
|
||||
$validated[$key] = $valid_values;
|
||||
break;
|
||||
|
||||
default:
|
||||
$validated[$key] = $value;
|
||||
|
||||
@@ -473,6 +473,14 @@ class ProductsController
|
||||
update_post_meta($product->get_id(), '_woonoow_subscription_signup_fee', self::sanitize_number($data['subscription_signup_fee']));
|
||||
}
|
||||
|
||||
// Affiliate meta
|
||||
if (isset($data['affiliate_enabled'])) {
|
||||
update_post_meta($product->get_id(), '_woonoow_affiliate_enabled', $data['affiliate_enabled'] ? 'yes' : 'no');
|
||||
}
|
||||
if (isset($data['affiliate_commission_rate'])) {
|
||||
update_post_meta($product->get_id(), '_woonoow_affiliate_commission_rate', self::sanitize_number($data['affiliate_commission_rate']));
|
||||
}
|
||||
|
||||
// Handle variations for variable products
|
||||
if ($type === 'variable' && !empty($data['attributes']) && is_array($data['attributes'])) {
|
||||
self::save_product_attributes($product, $data['attributes']);
|
||||
@@ -644,6 +652,14 @@ class ProductsController
|
||||
update_post_meta($product->get_id(), '_woonoow_subscription_signup_fee', self::sanitize_number($data['subscription_signup_fee']));
|
||||
}
|
||||
|
||||
// Affiliate meta
|
||||
if (isset($data['affiliate_enabled'])) {
|
||||
update_post_meta($product->get_id(), '_woonoow_affiliate_enabled', $data['affiliate_enabled'] ? 'yes' : 'no');
|
||||
}
|
||||
if (isset($data['affiliate_commission_rate'])) {
|
||||
update_post_meta($product->get_id(), '_woonoow_affiliate_commission_rate', self::sanitize_number($data['affiliate_commission_rate']));
|
||||
}
|
||||
|
||||
// Allow plugins to perform additional updates (Level 1 compatibility)
|
||||
do_action('woonoow/product_updated', $product, $data, $request);
|
||||
|
||||
@@ -857,6 +873,10 @@ class ProductsController
|
||||
$data['subscription_trial_days'] = get_post_meta($product->get_id(), '_woonoow_subscription_trial_days', true) ?: '';
|
||||
$data['subscription_signup_fee'] = get_post_meta($product->get_id(), '_woonoow_subscription_signup_fee', true) ?: '';
|
||||
|
||||
// Affiliate fields
|
||||
$data['affiliate_enabled'] = get_post_meta($product->get_id(), '_woonoow_affiliate_enabled', true) === 'yes';
|
||||
$data['affiliate_commission_rate'] = get_post_meta($product->get_id(), '_woonoow_affiliate_commission_rate', true) ?: '';
|
||||
|
||||
// Images array (URLs) for frontend - featured + gallery
|
||||
$images = [];
|
||||
$featured_image_id = $product->get_image_id();
|
||||
|
||||
@@ -110,6 +110,49 @@ class EventRegistry
|
||||
],
|
||||
],
|
||||
|
||||
// ===== AFFILIATE EVENTS =====
|
||||
'affiliate_application_received' => [
|
||||
'id' => 'affiliate_application_received',
|
||||
'label' => __('Affiliate Application Received', 'woonoow'),
|
||||
'description' => __('When a customer applies to be an affiliate', 'woonoow'),
|
||||
'category' => 'marketing',
|
||||
'recipient_type' => 'staff',
|
||||
'wc_email' => '',
|
||||
'enabled' => true,
|
||||
'variables' => [
|
||||
'{affiliate_name}' => __('Affiliate Name', 'woonoow'),
|
||||
'{customer_email}' => __('Customer Email', 'woonoow'),
|
||||
],
|
||||
],
|
||||
'affiliate_application_approved' => [
|
||||
'id' => 'affiliate_application_approved',
|
||||
'label' => __('Affiliate Application Approved', 'woonoow'),
|
||||
'description' => __('When an affiliate application is approved', 'woonoow'),
|
||||
'category' => 'marketing',
|
||||
'recipient_type' => 'customer',
|
||||
'wc_email' => '',
|
||||
'enabled' => true,
|
||||
'variables' => [
|
||||
'{affiliate_name}' => __('Affiliate Name', 'woonoow'),
|
||||
'{referral_code}' => __('Referral Code', 'woonoow'),
|
||||
],
|
||||
],
|
||||
'affiliate_new_referral' => [
|
||||
'id' => 'affiliate_new_referral',
|
||||
'label' => __('New Affiliate Referral', 'woonoow'),
|
||||
'description' => __('When an affiliate generates a new referral', 'woonoow'),
|
||||
'category' => 'marketing',
|
||||
'recipient_type' => 'customer',
|
||||
'wc_email' => '',
|
||||
'enabled' => true,
|
||||
'variables' => [
|
||||
'{affiliate_name}' => __('Affiliate Name', 'woonoow'),
|
||||
'{commission_amount}' => __('Commission Amount', 'woonoow'),
|
||||
'{currency}' => __('Currency', 'woonoow'),
|
||||
'{order_number}' => __('Order Number', 'woonoow'),
|
||||
],
|
||||
],
|
||||
|
||||
// ===== ORDER INITIATION =====
|
||||
'order_placed' => [
|
||||
'id' => 'order_placed',
|
||||
|
||||
@@ -91,6 +91,8 @@ class DefaultTemplates
|
||||
'order_refunded' => self::customer_order_refunded(),
|
||||
'new_customer' => self::customer_new_customer(),
|
||||
'newsletter_campaign' => self::customer_newsletter_campaign(),
|
||||
'affiliate_application_approved' => self::customer_affiliate_application_approved(),
|
||||
'affiliate_new_referral' => self::customer_affiliate_new_referral(),
|
||||
],
|
||||
'staff' => [
|
||||
'order_placed' => self::staff_order_placed(),
|
||||
@@ -104,6 +106,7 @@ class DefaultTemplates
|
||||
'order_failed' => self::staff_order_failed(),
|
||||
'order_cancelled' => self::staff_order_cancelled(),
|
||||
'order_refunded' => self::staff_order_refunded(),
|
||||
'affiliate_application_received' => self::staff_affiliate_application_received(),
|
||||
],
|
||||
];
|
||||
|
||||
@@ -141,6 +144,8 @@ class DefaultTemplates
|
||||
'order_refunded' => 'Refund processed for order #{order_number}',
|
||||
'new_customer' => 'Welcome to {site_name}! 🎁 Exclusive offer inside',
|
||||
'newsletter_campaign' => '{campaign_title}',
|
||||
'affiliate_application_approved' => 'You\'re approved! Welcome to the {site_name} Affiliate Program 🤝',
|
||||
'affiliate_new_referral' => 'Ka-ching! 💰 You just earned a new referral commission',
|
||||
],
|
||||
'staff' => [
|
||||
'order_placed' => '[NEW ORDER] #{order_number} - ${order_total} from {customer_name}',
|
||||
@@ -154,6 +159,7 @@ class DefaultTemplates
|
||||
'order_failed' => '[FAILED] #{order_number} - Unable to process',
|
||||
'order_cancelled' => '[CANCELLED] #{order_number} - Refund may be required',
|
||||
'order_refunded' => '[REFUNDED] #{order_number} - ${order_total} refunded to customer',
|
||||
'affiliate_application_received' => '[AFFILIATE APP] New application from {affiliate_name}',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -748,6 +754,62 @@ We hope to serve you again soon! Check out our new arrivals:
|
||||
[button url="{shop_url}"]Browse New Products[/button]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer: Affiliate Application Approved
|
||||
*/
|
||||
private static function customer_affiliate_application_approved()
|
||||
{
|
||||
return '[card type="hero"]
|
||||
## Welcome to the Affiliate Program, {affiliate_name}! 🤝
|
||||
|
||||
Your application has been approved. You can now start earning commissions by referring customers to {site_name}.
|
||||
[/card]
|
||||
|
||||
[card]
|
||||
**Your Details:**
|
||||
Referral Code: {referral_code}
|
||||
[/card]
|
||||
|
||||
[card type="success"]
|
||||
**How to Start Earning:**
|
||||
1. Go to your Affiliate Dashboard
|
||||
2. Copy your unique referral link
|
||||
3. Share it on your blog, social media, or with friends
|
||||
4. Earn commissions when they make a purchase!
|
||||
[/card]
|
||||
|
||||
[button url="{my_account_url}#/affiliate"]Go To Dashboard[/button]
|
||||
|
||||
[card type="basic"]
|
||||
Have questions about the program? Contact us at {support_email}
|
||||
[/card]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer: New Affiliate Referral
|
||||
*/
|
||||
private static function customer_affiliate_new_referral()
|
||||
{
|
||||
return '[card type="hero"]
|
||||
## You earned a commission! 💰
|
||||
|
||||
Great job, {affiliate_name}! A customer just placed an order using your referral link.
|
||||
[/card]
|
||||
|
||||
[card type="success"]
|
||||
**Referral Details:**
|
||||
Order Number: #{order_number}
|
||||
Commission Earned: {commission_amount} {currency}
|
||||
Status: Pending (Awaiting fulfillment)
|
||||
[/card]
|
||||
|
||||
[card]
|
||||
Commissions remain pending until the order is successfully completed and the return period has passed. You can track all your referrals and payouts in your dashboard.
|
||||
[/card]
|
||||
|
||||
[button url="{my_account_url}#/affiliate"]View Dashboard[/button]';
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// STAFF TEMPLATES
|
||||
// ========================================================================
|
||||
@@ -1215,4 +1277,33 @@ Refund Date: {order_date}
|
||||
Customer will see refund in their account within 5-10 business days depending on their bank. Flag if customer inquires about missing refund after 14 days.
|
||||
[/card]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Staff: Affiliate Application Received
|
||||
* Notifies staff when a new affiliate application is submitted
|
||||
*/
|
||||
private static function staff_affiliate_application_received()
|
||||
{
|
||||
return '[card type="info"]
|
||||
## 📝 New Affiliate Application
|
||||
|
||||
A new affiliate application has been submitted by {affiliate_name}. Please review the application details in the admin dashboard.
|
||||
[/card]
|
||||
|
||||
[card]
|
||||
**Application Details:**
|
||||
Name: {affiliate_name}
|
||||
Email: {affiliate_email}
|
||||
Date: {application_date}
|
||||
[/card]
|
||||
|
||||
[card type="info"]
|
||||
**Action Items:**
|
||||
☐ Review application details
|
||||
☐ Check applicant\'s social media or website
|
||||
☐ Approve or reject the application in the Woonoow Admin Dashboard
|
||||
[/card]
|
||||
|
||||
[button url="{admin_url}"]Review Application[/button]';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user