WebOrbiton
v1.0.0.1

StocketBase

207 lines · 7.7 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/includes/config.php';
  6. require_once __DIR__ . '/includes/database.php';
  7. require_once __DIR__ . '/includes/user-auth.php';
  8. require_once __DIR__ . '/includes/language.php';
  9. require_once __DIR__ . '/includes/site-front.php';
  10. require_once __DIR__ . '/includes/pagination.php';
  11. require_once __DIR__ . '/includes/product-card.php';
  12. ​
  13. UserAuth::boot();
  14. ​
  15. $db = Database::site();
  16. $settings = SiteFront::settings();
  17. ​
  18. function resolveFeaturedProducts(PDO $db, array $settings): array
  19. {
  20. if (($settings['featured_banner_enabled'] ?? '0') !== '1') {
  21. return [];
  22. }
  23. ​
  24. if (($settings['featured_banner_mode'] ?? 'static') === 'static') {
  25. $featuredIds = [];
  26. for ($i = 1; $i <= 5; $i++) {
  27. $id = (int) ($settings['featured_banner_product_' . $i] ?? 0);
  28. if ($id > 0 && !in_array($id, $featuredIds, true)) {
  29. $featuredIds[] = $id;
  30. }
  31. }
  32. ​
  33. if (empty($featuredIds)) {
  34. return [];
  35. }
  36. ​
  37. $placeholders = implode(',', array_fill(0, count($featuredIds), '?'));
  38. $statement = $db->prepare(
  39. "SELECT p.*, c.name AS category_name FROM products p LEFT JOIN categories c ON c.id = p.category_id WHERE p.status = 'published' AND p.id IN ($placeholders)"
  40. );
  41. $statement->execute($featuredIds);
  42. $byId = [];
  43. foreach ($statement->fetchAll() as $row) {
  44. $byId[(int) $row['id']] = $row;
  45. }
  46. ​
  47. $result = [];
  48. foreach ($featuredIds as $id) {
  49. if (isset($byId[$id])) {
  50. $result[] = $byId[$id];
  51. }
  52. }
  53. ​
  54. return $result;
  55. }
  56. ​
  57. $windowValue = max(1, min(720, (int) ($settings['featured_banner_auto_window_value'] ?? 24)));
  58. $windowUnit = ($settings['featured_banner_auto_window_unit'] ?? 'hours') === 'minutes' ? 'minutes' : 'hours';
  59. $windowMinutes = $windowUnit === 'hours' ? $windowValue * 60 : $windowValue;
  60. $cutoff = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->modify('-' . $windowMinutes . ' minutes')->format('Y-m-d H:i:s');
  61. ​
  62. $statement = $db->prepare(
  63. "SELECT p.*, c.name AS category_name FROM products p LEFT JOIN categories c ON c.id = p.category_id
  64. WHERE p.status = 'published' AND p.published_at >= :cutoff
  65. ORDER BY p.views_count DESC, p.published_at DESC, p.id DESC"
  66. );
  67. $statement->execute(['cutoff' => $cutoff]);
  68. $candidates = $statement->fetchAll();
  69. $categoryMode = $settings['featured_banner_auto_category_mode'] ?? 'mixed';
  70. ​
  71. $result = [];
  72. if ($categoryMode === 'one_per_category') {
  73. $used = [];
  74. foreach ($candidates as $row) {
  75. $key = (string) ($row['category_id'] ?? 'none');
  76. if (isset($used[$key])) {
  77. continue;
  78. }
  79. $used[$key] = true;
  80. $result[] = $row;
  81. if (count($result) === 5) {
  82. break;
  83. }
  84. }
  85. } elseif ($categoryMode === 'mixed') {
  86. $byCategory = [];
  87. foreach ($candidates as $row) {
  88. $key = (string) ($row['category_id'] ?? 'none');
  89. $byCategory[$key][] = $row;
  90. }
  91. while (count($result) < 5 && !empty($byCategory)) {
  92. foreach ($byCategory as $key => $rows) {
  93. if (!empty($rows)) {
  94. $result[] = array_shift($byCategory[$key]);
  95. }
  96. if (empty($byCategory[$key])) {
  97. unset($byCategory[$key]);
  98. }
  99. if (count($result) === 5) {
  100. break;
  101. }
  102. }
  103. }
  104. } else {
  105. $result = array_slice($candidates, 0, 5);
  106. }
  107. ​
  108. return $result;
  109. }
  110. ​
  111. function resolveFeaturedExcludedIds(array $settings, array $featuredProducts): array
  112. {
  113. if (($settings['featured_banner_enabled'] ?? '0') !== '1' || ($settings['featured_banner_hide_duplicates'] ?? '0') !== '1') {
  114. return [];
  115. }
  116. ​
  117. return array_values(array_map(static fn (array $product): int => (int) $product['id'], $featuredProducts));
  118. }
  119. ​
  120. $featuredProducts = resolveFeaturedProducts($db, $settings);
  121. $excludedIds = resolveFeaturedExcludedIds($settings, $featuredProducts);
  122. $excludeSql = empty($excludedIds) ? '' : ' AND p.id NOT IN (' . implode(',', $excludedIds) . ')';
  123. ​
  124. $currentPage = Pagination::currentPage();
  125. $totalProducts = (int) $db->query("SELECT COUNT(*) AS c FROM products p WHERE p.status = 'published'" . $excludeSql)->fetch()['c'];
  126. $isStaticProductLoading = ($settings['product_loading_mode'] ?? 'scroll') === 'static';
  127. $perPage = $isStaticProductLoading
  128. ? max(1, $totalProducts)
  129. : max(1, (int) $settings['products_per_page_home']);
  130. $currentPage = $isStaticProductLoading ? 1 : $currentPage;
  131. $offset = Pagination::offset($currentPage, $perPage);
  132. $totalPages = Pagination::totalPages($totalProducts, $perPage);
  133. ​
  134. $productsStatement = $db->prepare(
  135. "SELECT p.*, c.name AS category_name, c.slug AS category_slug FROM products p LEFT JOIN categories c ON c.id = p.category_id WHERE p.status = 'published'" . $excludeSql . " ORDER BY p.published_at DESC LIMIT :limit OFFSET :offset"
  136. );
  137. $productsStatement->bindValue('limit', $perPage, PDO::PARAM_INT);
  138. $productsStatement->bindValue('offset', $offset, PDO::PARAM_INT);
  139. $productsStatement->execute();
  140. $products = $productsStatement->fetchAll();
  141. ​
  142. $pageTitle = $settings['seo_title'] !== '' ? $settings['seo_title'] : $settings['site_name'];
  143. $pageDescription = $settings['seo_description'] !== '' ? $settings['seo_description'] : $settings['site_description'];
  144. $pageType = 'index';
  145. ​
  146. require_once __DIR__ . '/includes/block-editor.php';
  147. ​
  148. try {
  149. $homePage = SiteFront::homePage();
  150. } catch (Throwable $exception) {
  151. $homePage = null;
  152. }
  153. ​
  154. if ($homePage !== null) {
  155. $homePage = Languages::pages([$homePage])[0];
  156. $homeBlocksJson = (string) $homePage['content_blocks'];
  157. } else {
  158. $homeBlocksJson = BlockEditor::defaultHomepageBlocks(trim((string) ($settings['site_description'] ?? '')) !== '');
  159. }
  160. ​
  161. $renderHomeFeatured = static function () use ($settings, $featuredProducts): string {
  162. ob_start();
  163. foreach (SiteFront::activeAds('homepage-top') as $homepageTopAd) {
  164. echo SiteFront::renderAd($homepageTopAd);
  165. }
  166. if (($settings['featured_banner_enabled'] ?? '0') === '1') {
  167. echo '<div class="section-heading-row"><h2 class="section-heading">' . htmlspecialchars(Language::get('home_featured', 'Featured')) . '</h2></div>';
  168. if (!empty($featuredProducts)) {
  169. echo renderFeaturedBanner($featuredProducts);
  170. }
  171. }
  172. ​
  173. return (string) ob_get_clean();
  174. };
  175. ​
  176. $renderHomeProducts = static function () use ($settings, $products, $isStaticProductLoading, $currentPage, $totalPages): string {
  177. ob_start();
  178. $catalogTopAds = SiteFront::activeAds('catalog-top');
  179. echo '<div class="article-grid" id="article-list" data-pagination-source="home">';
  180. foreach ($products as $inFeedIndex => $product) {
  181. echo renderProductCard($product);
  182. if ($inFeedIndex === 2 && !empty($catalogTopAds)) {
  183. foreach ($catalogTopAds as $catalogTopAd) {
  184. echo SiteFront::renderAd($catalogTopAd);
  185. }
  186. }
  187. }
  188. if (empty($products)) {
  189. echo '<p>' . htmlspecialchars(Language::get('home_no_products', 'No products published yet.')) . '</p>';
  190. }
  191. echo '</div>';
  192. echo '<div id="pagination-container">';
  193. echo $isStaticProductLoading ? '' : Pagination::render($settings['pagination_style'], 'index.php', $currentPage, $totalPages, [], 'article-list');
  194. echo '</div>';
  195. ​
  196. return (string) ob_get_clean();
  197. };
  198. ​
  199. require __DIR__ . '/includes/front-header.php';
  200. ​
  201. echo BlockEditor::renderHomepage($homeBlocksJson, $settings, [
  202. 'home_featured' => $renderHomeFeatured,
  203. 'home_products' => $renderHomeProducts,
  204. ]);
  205. ​
  206. require __DIR__ . '/includes/front-footer.php';
  207. ​