Newsletter Fix: - Move all hooks (useQuery, useMutation) before conditional returns - Add 'enabled' option to useQuery to control when it fetches - Fixes React error #310: useEffect called conditionally - Newsletter page now loads without errors at /marketing/newsletter Wishlist Module Refactoring: - Create WishlistSettings.php with 8 configurable settings: * Enable guest wishlists * Wishlist page selector * Show in header toggle * Enable sharing * Back in stock notifications * Max items per wishlist * Multiple wishlists support * Show add to cart button - Add has_settings flag to wishlist module in ModuleRegistry - Initialize WishlistSettings in woonoow.php - Update customer-spa BaseLayout to use isEnabled('wishlist') check - Wishlist page already has module check (no changes needed) Files Added (1): - includes/Modules/WishlistSettings.php Files Modified (5): - admin-spa/src/routes/Marketing/Newsletter.tsx - includes/Core/ModuleRegistry.php - woonoow.php - customer-spa/src/layouts/BaseLayout.tsx - admin-spa/dist/app.js (rebuilt) Both newsletter and wishlist now follow the same module pattern: - Settings via schema (no code required) - Module enable/disable controls feature visibility - Settings page at /settings/modules/{module_id} - Consistent user experience
337 lines
11 KiB
PHP
337 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* Module Registry
|
|
*
|
|
* Central registry for managing WooNooW modules (features).
|
|
* Allows enabling/disabling modules to improve performance and reduce clutter.
|
|
*
|
|
* @package WooNooW\Core
|
|
*/
|
|
|
|
namespace WooNooW\Core;
|
|
|
|
class ModuleRegistry {
|
|
|
|
/**
|
|
* Get built-in modules
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function get_builtin_modules() {
|
|
$modules = [
|
|
'newsletter' => [
|
|
'id' => 'newsletter',
|
|
'label' => __('Newsletter & Campaigns', 'woonoow'),
|
|
'description' => __('Email newsletter subscription and campaign management', 'woonoow'),
|
|
'category' => 'marketing',
|
|
'icon' => 'mail',
|
|
'default_enabled' => true,
|
|
'has_settings' => true,
|
|
'features' => [
|
|
__('Subscriber management', 'woonoow'),
|
|
__('Email campaigns', 'woonoow'),
|
|
__('Campaign scheduling', 'woonoow'),
|
|
],
|
|
],
|
|
'wishlist' => [
|
|
'id' => 'wishlist',
|
|
'label' => __('Customer Wishlist', 'woonoow'),
|
|
'description' => __('Allow customers to save products for later', 'woonoow'),
|
|
'category' => 'customers',
|
|
'icon' => 'heart',
|
|
'default_enabled' => true,
|
|
'has_settings' => true,
|
|
'features' => [
|
|
__('Save products to wishlist', 'woonoow'),
|
|
__('Wishlist page', 'woonoow'),
|
|
__('Share wishlist', 'woonoow'),
|
|
],
|
|
],
|
|
'affiliate' => [
|
|
'id' => 'affiliate',
|
|
'label' => __('Affiliate Program', 'woonoow'),
|
|
'description' => __('Referral tracking and commission management', 'woonoow'),
|
|
'category' => 'marketing',
|
|
'icon' => 'users',
|
|
'default_enabled' => false,
|
|
'features' => [
|
|
__('Referral tracking', 'woonoow'),
|
|
__('Commission management', 'woonoow'),
|
|
__('Affiliate dashboard', 'woonoow'),
|
|
__('Payout system', 'woonoow'),
|
|
],
|
|
],
|
|
'subscription' => [
|
|
'id' => 'subscription',
|
|
'label' => __('Product Subscriptions', 'woonoow'),
|
|
'description' => __('Recurring product subscriptions with flexible billing', 'woonoow'),
|
|
'category' => 'products',
|
|
'icon' => 'refresh-cw',
|
|
'default_enabled' => false,
|
|
'features' => [
|
|
__('Recurring billing', 'woonoow'),
|
|
__('Subscription management', 'woonoow'),
|
|
__('Automatic renewals', 'woonoow'),
|
|
__('Trial periods', 'woonoow'),
|
|
],
|
|
],
|
|
'licensing' => [
|
|
'id' => 'licensing',
|
|
'label' => __('Software Licensing', 'woonoow'),
|
|
'description' => __('License key generation and validation for digital products', 'woonoow'),
|
|
'category' => 'products',
|
|
'icon' => 'key',
|
|
'default_enabled' => false,
|
|
'features' => [
|
|
__('License key generation', 'woonoow'),
|
|
__('Activation management', 'woonoow'),
|
|
__('Validation API', 'woonoow'),
|
|
__('Expiry management', 'woonoow'),
|
|
],
|
|
],
|
|
];
|
|
|
|
return $modules;
|
|
}
|
|
|
|
/**
|
|
* Get addon modules from AddonRegistry
|
|
*
|
|
* @return array
|
|
*/
|
|
private static function get_addon_modules() {
|
|
$addons = apply_filters('woonoow/addon_registry', []);
|
|
$modules = [];
|
|
|
|
foreach ($addons as $addon_id => $addon) {
|
|
$modules[$addon_id] = [
|
|
'id' => $addon_id,
|
|
'label' => $addon['name'] ?? ucfirst($addon_id),
|
|
'description' => $addon['description'] ?? '',
|
|
'category' => $addon['category'] ?? 'other',
|
|
'icon' => $addon['icon'] ?? 'puzzle',
|
|
'default_enabled' => false,
|
|
'features' => $addon['features'] ?? [],
|
|
'is_addon' => true,
|
|
'version' => $addon['version'] ?? '1.0.0',
|
|
'author' => $addon['author'] ?? '',
|
|
'has_settings' => !empty($addon['has_settings']),
|
|
'settings_component' => $addon['settings_component'] ?? null,
|
|
];
|
|
}
|
|
|
|
return $modules;
|
|
}
|
|
|
|
/**
|
|
* Get all modules (built-in + addons)
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_all_modules() {
|
|
$builtin = self::get_builtin_modules();
|
|
$addons = self::get_addon_modules();
|
|
|
|
return array_merge($builtin, $addons);
|
|
}
|
|
|
|
/**
|
|
* Get categories dynamically from registered modules
|
|
*
|
|
* @return array Associative array of category_id => label
|
|
*/
|
|
public static function get_categories() {
|
|
$all_modules = self::get_all_modules();
|
|
$categories = [];
|
|
|
|
// Extract unique categories from modules
|
|
foreach ($all_modules as $module) {
|
|
$cat = $module['category'] ?? 'other';
|
|
if (!isset($categories[$cat])) {
|
|
$categories[$cat] = self::get_category_label($cat);
|
|
}
|
|
}
|
|
|
|
// Sort by predefined order
|
|
$order = ['marketing', 'customers', 'products', 'shipping', 'payments', 'analytics', 'other'];
|
|
uksort($categories, function($a, $b) use ($order) {
|
|
$pos_a = array_search($a, $order);
|
|
$pos_b = array_search($b, $order);
|
|
if ($pos_a === false) $pos_a = 999;
|
|
if ($pos_b === false) $pos_b = 999;
|
|
return $pos_a - $pos_b;
|
|
});
|
|
|
|
return $categories;
|
|
}
|
|
|
|
/**
|
|
* Get human-readable label for category
|
|
*
|
|
* @param string $category Category ID
|
|
* @return string
|
|
*/
|
|
private static function get_category_label($category) {
|
|
$labels = [
|
|
'marketing' => __('Marketing & Sales', 'woonoow'),
|
|
'customers' => __('Customer Experience', 'woonoow'),
|
|
'products' => __('Products & Inventory', 'woonoow'),
|
|
'shipping' => __('Shipping & Fulfillment', 'woonoow'),
|
|
'payments' => __('Payments & Checkout', 'woonoow'),
|
|
'analytics' => __('Analytics & Reports', 'woonoow'),
|
|
'other' => __('Other Extensions', 'woonoow'),
|
|
];
|
|
|
|
return $labels[$category] ?? ucfirst($category);
|
|
}
|
|
|
|
/**
|
|
* Group modules by category
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_grouped_modules() {
|
|
$all_modules = self::get_all_modules();
|
|
$grouped = [];
|
|
|
|
foreach ($all_modules as $module) {
|
|
$cat = $module['category'] ?? 'other';
|
|
if (!isset($grouped[$cat])) {
|
|
$grouped[$cat] = [];
|
|
}
|
|
$grouped[$cat][] = $module;
|
|
}
|
|
|
|
return $grouped;
|
|
}
|
|
|
|
/**
|
|
* Get enabled modules
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_enabled_modules() {
|
|
$enabled = get_option('woonoow_enabled_modules', null);
|
|
|
|
// First time - use defaults
|
|
if ($enabled === null) {
|
|
$modules = self::get_all_modules();
|
|
$enabled = [];
|
|
foreach ($modules as $module) {
|
|
if ($module['default_enabled']) {
|
|
$enabled[] = $module['id'];
|
|
}
|
|
}
|
|
update_option('woonoow_enabled_modules', $enabled);
|
|
}
|
|
|
|
return $enabled;
|
|
}
|
|
|
|
/**
|
|
* Check if a module is enabled
|
|
*
|
|
* @param string $module_id
|
|
* @return bool
|
|
*/
|
|
public static function is_enabled($module_id) {
|
|
$enabled = self::get_enabled_modules();
|
|
return in_array($module_id, $enabled);
|
|
}
|
|
|
|
/**
|
|
* Enable a module
|
|
*
|
|
* @param string $module_id
|
|
* @return bool
|
|
*/
|
|
public static function enable($module_id) {
|
|
$modules = self::get_all_modules();
|
|
|
|
if (!isset($modules[$module_id])) {
|
|
return false;
|
|
}
|
|
|
|
$enabled = self::get_enabled_modules();
|
|
|
|
if (!in_array($module_id, $enabled)) {
|
|
$enabled[] = $module_id;
|
|
update_option('woonoow_enabled_modules', $enabled);
|
|
|
|
// Clear navigation cache when module is toggled
|
|
if (class_exists('\WooNooW\Compat\NavigationRegistry')) {
|
|
\WooNooW\Compat\NavigationRegistry::flush();
|
|
}
|
|
|
|
do_action('woonoow/module/enabled', $module_id);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Disable a module
|
|
*
|
|
* @param string $module_id
|
|
* @return bool
|
|
*/
|
|
public static function disable($module_id) {
|
|
$enabled = self::get_enabled_modules();
|
|
|
|
if (in_array($module_id, $enabled)) {
|
|
$enabled = array_diff($enabled, [$module_id]);
|
|
update_option('woonoow_enabled_modules', array_values($enabled));
|
|
|
|
// Clear navigation cache when module is toggled
|
|
if (class_exists('\WooNooW\Compat\NavigationRegistry')) {
|
|
\WooNooW\Compat\NavigationRegistry::flush();
|
|
}
|
|
|
|
do_action('woonoow/module/disabled', $module_id);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get modules by category
|
|
*
|
|
* @param string $category
|
|
* @return array
|
|
*/
|
|
public static function get_by_category($category) {
|
|
$modules = self::get_all_modules();
|
|
$enabled = self::get_enabled_modules();
|
|
|
|
$result = [];
|
|
foreach ($modules as $module) {
|
|
if ($module['category'] === $category) {
|
|
$module['enabled'] = in_array($module['id'], $enabled);
|
|
$result[] = $module;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get all modules with enabled status
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_all_with_status() {
|
|
$modules = self::get_all_modules();
|
|
$enabled = self::get_enabled_modules();
|
|
|
|
foreach ($modules as $id => $module) {
|
|
$modules[$id]['enabled'] = in_array($id, $enabled);
|
|
}
|
|
|
|
return $modules;
|
|
}
|
|
}
|