prepare(" SELECT item_label AS title, units, revenue FROM ac_store_sales_chart_cache WHERE chart_scope = :scope AND chart_window = :window ORDER BY rank_no ASC LIMIT :limit "); $stmt->bindValue(':scope', $scope, \PDO::PARAM_STR); $stmt->bindValue(':window', $window, \PDO::PARAM_STR); $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); $stmt->execute(); $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC) ?: []; } catch (\Throwable $e) { $rows = []; } if (!$rows) { try { $controller = new StoreController(); $controller->rebuildSalesChartCache(); $stmt = $db->prepare(" SELECT item_label AS title, units, revenue FROM ac_store_sales_chart_cache WHERE chart_scope = :scope AND chart_window = :window ORDER BY rank_no ASC LIMIT :limit "); $stmt->bindValue(':scope', $scope, \PDO::PARAM_STR); $stmt->bindValue(':window', $window, \PDO::PARAM_STR); $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); $stmt->execute(); $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC) ?: []; } catch (\Throwable $e) { $rows = []; } } if (!$rows) { return '
No sales yet.
'; } $currency = strtoupper(trim((string)Settings::get('store_currency', 'GBP'))); if (!preg_match('/^[A-Z]{3}$/', $currency)) { $currency = 'GBP'; } $list = ''; $position = 1; foreach ($rows as $row) { $title = htmlspecialchars((string)($row['title'] ?? ''), ENT_QUOTES, 'UTF-8'); $units = (int)($row['units'] ?? 0); $revenue = number_format((float)($row['revenue'] ?? 0), 2); $list .= '
  • ' . '#' . $position . '' . '' . $title . '' . '' . $units . ' sold - ' . htmlspecialchars($currency, ENT_QUOTES, 'UTF-8') . ' ' . $revenue . '' . '
  • '; $position++; } return '
      ' . $list . '
    '; }); Shortcodes::register('top-sellers', static function (array $attrs = []): string { $type = trim((string)($attrs['type'] ?? 'tracks')); $window = trim((string)($attrs['window'] ?? 'latest')); $limit = max(1, min(50, (int)($attrs['limit'] ?? 10))); return Shortcodes::render('[sale-chart type="' . $type . '" window="' . $window . '" limit="' . $limit . '"]'); }); Shortcodes::register('login-link', static function (array $attrs = []): string { $label = trim((string)($attrs['label'] ?? 'Login')); if ($label === '') { $label = 'Login'; } return '' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . ''; }); Shortcodes::register('account-link', static function (array $attrs = []): string { $label = trim((string)($attrs['label'] ?? 'My Account')); if ($label === '') { $label = 'My Account'; } return '' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . ''; }); Shortcodes::register('checkout-link', static function (array $attrs = []): string { $label = trim((string)($attrs['label'] ?? 'Checkout')); if ($label === '') { $label = 'Checkout'; } return '' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . ''; }); Shortcodes::register('cart-link', static function (array $attrs = []): string { $showCount = ((string)($attrs['show_count'] ?? '1')) !== '0'; $showTotal = ((string)($attrs['show_total'] ?? '1')) !== '0'; $label = trim((string)($attrs['label'] ?? 'Cart')); if ($label === '') { $label = 'Cart'; } $count = 0; $amount = 0.0; $currency = strtoupper(trim((string)Settings::get('store_currency', 'GBP'))); if (!preg_match('/^[A-Z]{3}$/', $currency)) { $currency = 'GBP'; } if (session_status() !== PHP_SESSION_ACTIVE) { @session_start(); } $cart = is_array($_SESSION['ac_cart'] ?? null) ? $_SESSION['ac_cart'] : []; foreach ($cart as $item) { if (!is_array($item)) { continue; } $qty = max(1, (int)($item['qty'] ?? 1)); $price = (float)($item['price'] ?? 0); $count += $qty; $amount += ($price * $qty); } $parts = [htmlspecialchars($label, ENT_QUOTES, 'UTF-8')]; if ($showCount) { $parts[] = '' . $count . ''; } if ($showTotal) { $parts[] = '' . htmlspecialchars($currency, ENT_QUOTES, 'UTF-8') . ' ' . number_format($amount, 2) . ''; } return '' . implode(' ', $parts) . ''; }); return function (Router $router): void { $controller = new StoreController(); $router->get('/cart', [$controller, 'cartIndex']); $router->post('/cart/discount/apply', [$controller, 'cartApplyDiscount']); $router->post('/cart/discount/remove', [$controller, 'cartClearDiscount']); $router->get('/checkout', [$controller, 'checkoutIndex']); $router->post('/checkout/card/start', [$controller, 'checkoutCardStart']); $router->get('/checkout/card', [$controller, 'checkoutCard']); $router->get('/account', [$controller, 'accountIndex']); $router->post('/account/request-login', [$controller, 'accountRequestLogin']); $router->get('/account/login', [$controller, 'accountLogin']); $router->get('/account/logout', [$controller, 'accountLogout']); $router->post('/checkout/place', [$controller, 'checkoutPlace']); $router->post('/checkout/paypal/create-order', [$controller, 'checkoutPaypalCreateOrder']); $router->post('/checkout/paypal/capture-order', [$controller, 'checkoutPaypalCaptureJson']); $router->get('/checkout/paypal/return', [$controller, 'checkoutPaypalReturn']); $router->get('/checkout/paypal/cancel', [$controller, 'checkoutPaypalCancel']); $router->post('/checkout/sandbox', [$controller, 'checkoutSandbox']); $router->get('/store/download', [$controller, 'download']); $router->post('/cart/remove', [$controller, 'cartRemove']); $router->get('/store/sales-chart/rebuild', [$controller, 'salesChartCron']); $router->get('/admin/store', [$controller, 'adminIndex']); $router->post('/admin/store/install', [$controller, 'adminInstall']); $router->get('/admin/store/settings', [$controller, 'adminSettings']); $router->post('/admin/store/settings', [$controller, 'adminSaveSettings']); $router->post('/admin/store/settings/rebuild-sales-chart', [$controller, 'adminRebuildSalesChart']); $router->post('/admin/store/discounts/create', [$controller, 'adminDiscountCreate']); $router->post('/admin/store/discounts/delete', [$controller, 'adminDiscountDelete']); $router->post('/admin/store/bundles/create', [$controller, 'adminBundleCreate']); $router->post('/admin/store/bundles/delete', [$controller, 'adminBundleDelete']); $router->post('/admin/store/settings/test-email', [$controller, 'adminSendTestEmail']); $router->post('/admin/store/settings/test-paypal', [$controller, 'adminTestPaypal']); $router->get('/admin/store/customers', [$controller, 'adminCustomers']); $router->get('/admin/store/orders', [$controller, 'adminOrders']); $router->post('/admin/store/orders/create', [$controller, 'adminOrderCreate']); $router->post('/admin/store/orders/status', [$controller, 'adminOrderStatus']); $router->post('/admin/store/orders/refund', [$controller, 'adminOrderRefund']); $router->post('/admin/store/orders/delete', [$controller, 'adminOrderDelete']); $router->get('/admin/store/order', [$controller, 'adminOrderView']); $router->post('/store/cart/add', [$controller, 'cartAdd']); };