feat/fix: checkout email tracing, UI tweaks for add-to-cart, cart page overflow fix, implement hide admin bar setting

This commit is contained in:
Dwindi Ramadhana
2026-02-27 23:15:10 +07:00
parent 687a2318b0
commit a62037d993
22 changed files with 2711 additions and 294 deletions

View File

@@ -93,18 +93,13 @@ class OnboardingController extends WP_REST_Controller
if (!empty($params['create_home_page']) && $params['create_home_page'] === true) {
$page_id = $this->create_magic_homepage();
if ($page_id) {
update_option('page_on_front', $page_id);
update_option('show_on_front', 'page');
// Set as SPA entry page
// Set as SPA entry page only (don't modify WP front page)
update_option('woonoow_spa_entry_page', $page_id);
}
} elseif (!empty($params['entry_page_id'])) {
$page_id = absint($params['entry_page_id']);
// Set as SPA entry page only (don't modify WP front page)
update_option('woonoow_spa_entry_page', $page_id);
// Optionally set as front page if requested? The user just selected "Where should customers land".
// Let's assume for the wizard flow, selecting it implies setting it as front page too for consistency.
update_option('page_on_front', $page_id);
update_option('show_on_front', 'page');
}
// 3. Appearance Settings

View File

@@ -28,6 +28,7 @@ use WooNooW\Api\CampaignsController;
use WooNooW\Api\DocsController;
use WooNooW\Api\LicensesController;
use WooNooW\Api\SubscriptionsController;
use WooNooW\Api\SoftwareController;
use WooNooW\Frontend\ShopController;
use WooNooW\Frontend\CartController as FrontendCartController;
use WooNooW\Frontend\AccountController;
@@ -171,6 +172,9 @@ class Routes
// Subscriptions controller (subscription module)
SubscriptionsController::register_routes();
// Software controller (software distribution module)
SoftwareController::register_routes();
// Modules controller
$modules_controller = new ModulesController();
$modules_controller->register_routes();

View File

@@ -0,0 +1,321 @@
<?php
/**
* Software Distribution API Controller
*
* REST API endpoints for software update checking and downloads.
*
* @package WooNooW\Api
*/
namespace WooNooW\Api;
if (!defined('ABSPATH')) exit;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
use WooNooW\Core\ModuleRegistry;
use WooNooW\Modules\Software\SoftwareManager;
use WooNooW\Modules\Licensing\LicenseManager;
class SoftwareController
{
/**
* Register REST routes
*/
public static function register_routes()
{
$namespace = 'woonoow/v1';
// Public endpoints (authenticated via license key)
// Check for updates
register_rest_route($namespace, '/software/check', [
'methods' => 'GET',
'callback' => [__CLASS__, 'check_update'],
'permission_callback' => '__return_true',
'args' => [
'license_key' => ['required' => true, 'type' => 'string'],
'slug' => ['required' => true, 'type' => 'string'],
'version' => ['required' => true, 'type' => 'string'],
'site_url' => ['required' => false, 'type' => 'string'],
],
]);
// Also support POST for update check (some clients prefer this)
register_rest_route($namespace, '/software/check', [
'methods' => 'POST',
'callback' => [__CLASS__, 'check_update'],
'permission_callback' => '__return_true',
]);
// Download file
register_rest_route($namespace, '/software/download', [
'methods' => 'GET',
'callback' => [__CLASS__, 'download'],
'permission_callback' => '__return_true',
'args' => [
'token' => ['required' => true, 'type' => 'string'],
],
]);
// Get changelog
register_rest_route($namespace, '/software/changelog', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_changelog'],
'permission_callback' => '__return_true',
'args' => [
'slug' => ['required' => true, 'type' => 'string'],
'version' => ['required' => false, 'type' => 'string'],
],
]);
// Admin endpoints (requires manage_woocommerce)
// Get all versions for a product
register_rest_route($namespace, '/software/products/(?P<product_id>\d+)/versions', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_versions'],
'permission_callback' => function () {
return current_user_can('manage_woocommerce');
},
]);
// Add new version
register_rest_route($namespace, '/software/products/(?P<product_id>\d+)/versions', [
'methods' => 'POST',
'callback' => [__CLASS__, 'add_version'],
'permission_callback' => function () {
return current_user_can('manage_woocommerce');
},
]);
}
/**
* Check for updates
*/
public static function check_update(WP_REST_Request $request)
{
// Check if module is enabled
if (!ModuleRegistry::is_enabled('software')) {
return new WP_REST_Response([
'success' => false,
'error' => 'module_disabled',
'message' => __('Software distribution module is not enabled', 'woonoow'),
], 503);
}
// Get parameters from GET or POST body
$params = $request->get_method() === 'POST'
? $request->get_json_params()
: $request->get_query_params();
$license_key = sanitize_text_field($params['license_key'] ?? '');
$slug = sanitize_text_field($params['slug'] ?? '');
$current_version = sanitize_text_field($params['version'] ?? '');
$site_url = esc_url_raw($params['site_url'] ?? '');
if (empty($license_key) || empty($slug) || empty($current_version)) {
return new WP_REST_Response([
'success' => false,
'error' => 'missing_params',
'message' => __('Missing required parameters: license_key, slug, version', 'woonoow'),
], 400);
}
// Log the check (optional - for analytics)
do_action('woonoow/software/update_check', $slug, $current_version, $site_url, $license_key);
$result = SoftwareManager::check_update($license_key, $slug, $current_version);
$status_code = isset($result['success']) && $result['success'] === false ? 400 : 200;
return new WP_REST_Response($result, $status_code);
}
/**
* Download file
*/
public static function download(WP_REST_Request $request)
{
// Check if module is enabled
if (!ModuleRegistry::is_enabled('software')) {
return new WP_REST_Response([
'success' => false,
'error' => 'module_disabled',
'message' => __('Software distribution module is not enabled', 'woonoow'),
], 503);
}
$token = sanitize_text_field($request->get_param('token'));
if (empty($token)) {
return new WP_REST_Response([
'success' => false,
'error' => 'missing_token',
'message' => __('Download token is required', 'woonoow'),
], 400);
}
// Validate token
$download = SoftwareManager::validate_download_token($token);
if (is_wp_error($download)) {
return new WP_REST_Response([
'success' => false,
'error' => $download->get_error_code(),
'message' => $download->get_error_message(),
], 403);
}
// Validate license is still active
$license = LicenseManager::get_license($download['license_id']);
if (!$license || $license['status'] !== 'active') {
return new WP_REST_Response([
'success' => false,
'error' => 'license_inactive',
'message' => __('License is no longer active', 'woonoow'),
], 403);
}
// Serve the file
SoftwareManager::serve_file($download['product_id']);
// Note: serve_file calls exit, so this won't be reached
return new WP_REST_Response(['success' => true], 200);
}
/**
* Get changelog
*/
public static function get_changelog(WP_REST_Request $request)
{
$slug = sanitize_text_field($request->get_param('slug'));
$version = sanitize_text_field($request->get_param('version'));
if (empty($slug)) {
return new WP_REST_Response([
'success' => false,
'error' => 'missing_slug',
'message' => __('Software slug is required', 'woonoow'),
], 400);
}
// Get product by slug
$product = SoftwareManager::get_product_by_slug($slug);
if (!$product) {
return new WP_REST_Response([
'success' => false,
'error' => 'product_not_found',
'message' => __('Software product not found', 'woonoow'),
], 404);
}
// Get all versions or specific version
if ($version) {
$changelog = SoftwareManager::get_version_changelog($product->get_id(), $version);
if (!$changelog) {
return new WP_REST_Response([
'success' => false,
'error' => 'version_not_found',
'message' => __('Version not found', 'woonoow'),
], 404);
}
return new WP_REST_Response([
'slug' => $slug,
'version' => $changelog['version'],
'release_date' => $changelog['release_date'],
'changelog' => $changelog['changelog'],
], 200);
}
// Get all versions
$versions = SoftwareManager::get_all_versions($product->get_id());
return new WP_REST_Response([
'slug' => $slug,
'versions' => array_map(function ($v) {
return [
'version' => $v['version'],
'release_date' => $v['release_date'],
'changelog' => $v['changelog'],
'download_count' => (int) $v['download_count'],
];
}, $versions),
], 200);
}
/**
* Get versions for a product (admin)
*/
public static function get_versions(WP_REST_Request $request)
{
$product_id = (int) $request->get_param('product_id');
$product = wc_get_product($product_id);
if (!$product) {
return new WP_REST_Response([
'success' => false,
'error' => 'product_not_found',
], 404);
}
$versions = SoftwareManager::get_all_versions($product_id);
$config = SoftwareManager::get_product_config($product_id);
return new WP_REST_Response([
'product_id' => $product_id,
'config' => $config,
'versions' => $versions,
], 200);
}
/**
* Add new version (admin)
*/
public static function add_version(WP_REST_Request $request)
{
$product_id = (int) $request->get_param('product_id');
$params = $request->get_json_params();
$version = sanitize_text_field($params['version'] ?? '');
$changelog = wp_kses_post($params['changelog'] ?? '');
$set_current = (bool) ($params['set_current'] ?? true);
if (empty($version)) {
return new WP_REST_Response([
'success' => false,
'error' => 'missing_version',
'message' => __('Version number is required', 'woonoow'),
], 400);
}
$product = wc_get_product($product_id);
if (!$product) {
return new WP_REST_Response([
'success' => false,
'error' => 'product_not_found',
], 404);
}
$result = SoftwareManager::add_version($product_id, $version, $changelog, $set_current);
if (is_wp_error($result)) {
return new WP_REST_Response([
'success' => false,
'error' => $result->get_error_code(),
'message' => $result->get_error_message(),
], 400);
}
return new WP_REST_Response([
'success' => true,
'version_id' => $result,
'message' => __('Version added successfully', 'woonoow'),
], 201);
}
}