From c5302c3b44bd1a6b7e79dd9aeb5940f16ef923d9 Mon Sep 17 00:00:00 2001 From: AudioCore Bot Date: Thu, 5 Mar 2026 14:18:20 +0000 Subject: [PATCH] Schedule releases by release_date and block unreleased store purchases --- plugins/releases/ReleasesController.php | 20 +++- plugins/releases/plugin.php | 2 + plugins/store/StoreController.php | 122 +++++++++++++++++++++++- 3 files changed, 139 insertions(+), 5 deletions(-) diff --git a/plugins/releases/ReleasesController.php b/plugins/releases/ReleasesController.php index e86c658..6e79f95 100644 --- a/plugins/releases/ReleasesController.php +++ b/plugins/releases/ReleasesController.php @@ -46,6 +46,7 @@ class ReleasesController } if ($db instanceof PDO) { try { + $today = date('Y-m-d'); $stmt = $db->prepare("SELECT title, content_html FROM ac_pages WHERE slug = 'releases' AND is_published = 1 LIMIT 1"); $stmt->execute(); $page = $stmt->fetch(PDO::FETCH_ASSOC) ?: null; @@ -58,7 +59,8 @@ class ReleasesController } $params = []; - $where = ["r.is_published = 1"]; + $where = ["r.is_published = 1", "(r.release_date IS NULL OR r.release_date <= :today)"]; + $params[':today'] = $today; if ($artistFilter !== '') { if ($artistJoinReady) { $where[] = "(r.artist_name = :artist OR a.name = :artist)"; @@ -109,6 +111,7 @@ class ReleasesController FROM ac_releases r LEFT JOIN ac_artists a ON a.id = r.artist_id WHERE r.is_published = 1 + AND (r.release_date IS NULL OR r.release_date <= CURDATE()) ORDER BY artist_name ASC "); } else { @@ -144,6 +147,7 @@ class ReleasesController SELECT DISTINCT TRIM(artist_name) AS artist_name FROM ac_releases WHERE is_published = 1 + AND (release_date IS NULL OR release_date <= CURDATE()) ORDER BY artist_name ASC "); } @@ -188,8 +192,18 @@ class ReleasesController $db = Database::get(); if ($db instanceof PDO) { try { - $stmt = $db->prepare("SELECT * FROM ac_releases WHERE slug = :slug AND is_published = 1 LIMIT 1"); - $stmt->execute([':slug' => $slug]); + $stmt = $db->prepare(" + SELECT * + FROM ac_releases + WHERE slug = :slug + AND is_published = 1 + AND (release_date IS NULL OR release_date <= :today) + LIMIT 1 + "); + $stmt->execute([ + ':slug' => $slug, + ':today' => date('Y-m-d'), + ]); $release = $stmt->fetch(PDO::FETCH_ASSOC) ?: null; if ($release) { if ($storePluginEnabled) { diff --git a/plugins/releases/plugin.php b/plugins/releases/plugin.php index e6fb1b2..0e7daea 100644 --- a/plugins/releases/plugin.php +++ b/plugins/releases/plugin.php @@ -30,6 +30,7 @@ Shortcodes::register('releases', static function (array $attrs = []): string { FROM ac_releases r LEFT JOIN ac_artists a ON a.id = r.artist_id WHERE r.is_published = 1 + AND (r.release_date IS NULL OR r.release_date <= CURDATE()) ORDER BY r.release_date DESC, r.created_at DESC LIMIT :limit "); @@ -38,6 +39,7 @@ Shortcodes::register('releases', static function (array $attrs = []): string { SELECT title, slug, release_date, cover_url, artist_name FROM ac_releases WHERE is_published = 1 + AND (release_date IS NULL OR release_date <= CURDATE()) ORDER BY release_date DESC, created_at DESC LIMIT :limit "); diff --git a/plugins/store/StoreController.php b/plugins/store/StoreController.php index 8b34582..21d3069 100644 --- a/plugins/store/StoreController.php +++ b/plugins/store/StoreController.php @@ -1326,8 +1326,18 @@ class StoreController $cart = []; } + $db = Database::get(); + if ($db instanceof PDO) { + if (!$this->isItemReleased($db, $itemType, $itemId)) { + $_SESSION['ac_site_notice'] = [ + 'type' => 'info', + 'text' => 'This release is scheduled and is not available yet.', + ]; + return new Response('', 302, ['Location' => $this->safeReturnUrl($returnUrl)]); + } + } + if ($itemType === 'release') { - $db = Database::get(); if ($db instanceof PDO) { try { $trackStmt = $db->prepare(" @@ -1483,6 +1493,31 @@ class StoreController })); $db = Database::get(); + if ($db instanceof PDO) { + $filtered = []; + $removed = 0; + foreach ($items as $item) { + $itemType = (string)($item['item_type'] ?? 'track'); + $itemId = (int)($item['item_id'] ?? 0); + if ($itemId > 0 && $this->isItemReleased($db, $itemType, $itemId)) { + $filtered[] = $item; + continue; + } + $removed++; + $key = (string)($item['key'] ?? ''); + if ($key !== '' && isset($_SESSION['ac_cart'][$key])) { + unset($_SESSION['ac_cart'][$key]); + } + } + if ($removed > 0) { + $_SESSION['ac_site_notice'] = [ + 'type' => 'info', + 'text' => 'Unreleased items were removed from your cart.', + ]; + } + $items = $filtered; + } + if ($db instanceof PDO) { foreach ($items as $idx => $item) { $cover = trim((string)($item['cover_url'] ?? '')); @@ -1562,6 +1597,23 @@ class StoreController $items = array_values(array_filter($cart, static function ($item): bool { return is_array($item); })); + $db = Database::get(); + if ($db instanceof PDO) { + $filtered = []; + foreach ($items as $item) { + $itemType = (string)($item['item_type'] ?? 'track'); + $itemId = (int)($item['item_id'] ?? 0); + if ($itemId > 0 && $this->isItemReleased($db, $itemType, $itemId)) { + $filtered[] = $item; + continue; + } + $key = (string)($item['key'] ?? ''); + if ($key !== '' && isset($_SESSION['ac_cart'][$key])) { + unset($_SESSION['ac_cart'][$key]); + } + } + $items = $filtered; + } $discountCode = strtoupper(trim((string)($_SESSION['ac_discount_code'] ?? ''))); $totals = $this->buildCartTotals($items, $discountCode); if ($totals['discount_code'] === '') { @@ -1577,7 +1629,6 @@ class StoreController $success = (string)($_GET['success'] ?? ''); $orderNo = (string)($_GET['order_no'] ?? ''); if ($success !== '' && $orderNo !== '') { - $db = Database::get(); if ($db instanceof PDO) { try { $orderStmt = $db->prepare("SELECT id, status, email FROM ac_store_orders WHERE order_no = :order_no LIMIT 1"); @@ -1682,6 +1733,32 @@ class StoreController if (!($db instanceof PDO)) { return new Response('', 302, ['Location' => '/checkout']); } + $validItems = []; + $removed = 0; + foreach ($items as $item) { + $itemType = (string)($item['item_type'] ?? 'track'); + $itemId = (int)($item['item_id'] ?? 0); + if ($itemId > 0 && $this->isItemReleased($db, $itemType, $itemId)) { + $validItems[] = $item; + continue; + } + $removed++; + $key = (string)($item['key'] ?? ''); + if ($key !== '' && isset($_SESSION['ac_cart'][$key])) { + unset($_SESSION['ac_cart'][$key]); + } + } + if (!$validItems) { + return new Response('', 302, ['Location' => '/checkout?error=Selected+items+are+not+yet+released']); + } + if ($removed > 0) { + $_SESSION['ac_site_notice'] = [ + 'type' => 'info', + 'text' => 'Some unreleased items were removed from your cart.', + ]; + } + $items = $validItems; + $this->ensureAnalyticsSchema(); $customerIp = $this->clientIp(); $customerUserAgent = substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255); @@ -2775,6 +2852,47 @@ class StoreController return $scheme . '://' . $host; } + private function isItemReleased(PDO $db, string $itemType, int $itemId): bool + { + if ($itemId <= 0) { + return false; + } + try { + if ($itemType === 'release') { + $stmt = $db->prepare(" + SELECT 1 + FROM ac_releases + WHERE id = :id + AND is_published = 1 + AND (release_date IS NULL OR release_date <= :today) + LIMIT 1 + "); + $stmt->execute([ + ':id' => $itemId, + ':today' => date('Y-m-d'), + ]); + return (bool)$stmt->fetch(PDO::FETCH_ASSOC); + } + + $stmt = $db->prepare(" + SELECT 1 + FROM ac_release_tracks t + JOIN ac_releases r ON r.id = t.release_id + WHERE t.id = :id + AND r.is_published = 1 + AND (r.release_date IS NULL OR r.release_date <= :today) + LIMIT 1 + "); + $stmt->execute([ + ':id' => $itemId, + ':today' => date('Y-m-d'), + ]); + return (bool)$stmt->fetch(PDO::FETCH_ASSOC); + } catch (Throwable $e) { + return false; + } + } + private function logMailDebug(string $ref, array $payload): void { try {