WebOrbiton
v1.0.0.0

StocketBase

218 lines · 10.3 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/icons.php';
  6. require_once __DIR__ . '/asset.php';
  7. require_once __DIR__ . '/csrf.php';
  8. require_once __DIR__ . '/cart.php';
  9. ​
  10. $cartItemCount = Cart::count();
  11. $cartIsGuest = !UserAuth::check();
  12. ​
  13. function renderShopIcon(array $settings, string $extraClass = 'icon'): string
  14. {
  15. $choice = (string) ($settings['shop_icon_choice'] ?? 'cart');
  16. if ($choice === 'custom') {
  17. $customSvg = (string) ($settings['shop_icon_custom_svg'] ?? '');
  18. if ($customSvg !== '') {
  19. return '<span class="' . htmlspecialchars($extraClass, ENT_QUOTES) . ' shop-icon-custom" aria-hidden="true">' . $customSvg . '</span>';
  20. }
  21. $choice = 'cart';
  22. }
  23. ​
  24. return Icons::icon($choice, $extraClass);
  25. }
  26. $cartExportItems = $cartIsGuest ? array_map(
  27. static fn(array $item): array => [
  28. 'product_id' => $item['product_id'],
  29. 'variant_id' => $item['variant_id'],
  30. 'quantity' => $item['quantity'],
  31. ],
  32. Cart::items()
  33. ) : [];
  34. ​
  35. if (Languages::current() !== '') {
  36. ob_start([Languages::class, 'absolutizeUrls']);
  37. }
  38. ​
  39. $navItems = SiteFront::navItems();
  40. ​
  41. $menuPositionClass = 'site-menu-' . ($settings['menu_position'] ?? 'top');
  42. ​
  43. $resolvedPageTitle = $pageTitle ?? $settings['site_name'];
  44. $resolvedPageDescription = $pageDescription ?? '';
  45. $isHomepage = ($_SERVER['REQUEST_URI'] ?? '') === '/' || basename((string) ($_SERVER['SCRIPT_NAME'] ?? '')) === 'index.php';
  46. ​
  47. ?>
  48. <!DOCTYPE html>
  49. <html lang="<?= htmlspecialchars(Languages::htmlLang(), ENT_QUOTES) ?>">
  50. ​
  51. <head>
  52. <meta charset="utf-8">
  53. <meta name="viewport" content="width=device-width, initial-scale=1">
  54. <title><?= htmlspecialchars($resolvedPageTitle) ?></title>
  55. <?php if (!empty($resolvedPageDescription)): ?>
  56. <meta name="description" content="<?= htmlspecialchars($resolvedPageDescription) ?>">
  57. <?php endif; ?>
  58. <?= SiteFront::renderSeoMeta() ?>
  59. <?= Languages::renderAlternates() ?>
  60. <?= SiteFront::renderOpenGraph($resolvedPageTitle, $resolvedPageDescription, $ogType ?? 'website', $ogImagePath ?? null) ?>
  61. <?php if ($isHomepage): ?>
  62. <?= SiteFront::renderOrganizationSchema() ?>
  63. <?php endif; ?>
  64. <?= Asset::favicon() ?>
  65. <?php if (($settings['pwa_enabled'] ?? '0') === '1'): ?>
  66. <?php $pwaIconUrl = !empty($settings['pwa_icon_url']) ? $settings['pwa_icon_url'] : ($settings['site_logo_url'] ?? ''); ?>
  67. <link rel="manifest" href="<?= htmlspecialchars(rtrim(Config::get('APP_BASE_PATH', ''), '/')) ?>/manifest.json">
  68. <meta name="theme-color" content="<?= htmlspecialchars($settings['pwa_theme_color'] ?: '#ffffff') ?>">
  69. <?php if (!empty($pwaIconUrl)): ?>
  70. <link rel="apple-touch-icon" href="<?= htmlspecialchars($pwaIconUrl) ?>">
  71. <?php endif; ?>
  72. <?php endif; ?>
  73. <?= Asset::css('assets/site.css') ?>
  74. <style>
  75. <?= SiteFront::renderFontFaces() ?><?= SiteFront::fontVariablesCss() ?>
  76. </style>
  77. <?php if (!empty($settings['custom_css'])): ?>
  78. <style>
  79. <?= $settings['custom_css'] ?>
  80. </style>
  81. <?php endif; ?>
  82. <?= SiteFront::renderCustomHeadHtml($pageType ?? null) ?>
  83. <?= SiteFront::renderAnalyticsScripts('head') ?>
  84. </head>
  85. ​
  86. <body class="<?= htmlspecialchars($menuPositionClass) ?>" data-theme="light">
  87. <?= SiteFront::renderAnalyticsScripts('body_start') ?>
  88. <script>
  89. (function() {
  90. try {
  91. var t = localStorage.getItem('stocketbase_theme');
  92. if (t === 'dark' || t === 'light') {
  93. document.body.setAttribute('data-theme', t);
  94. } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
  95. document.body.setAttribute('data-theme', 'dark');
  96. }
  97. } catch (e) {}
  98. })();
  99. </script>
  100. <?php if ($cartIsGuest): ?>
  101. <script>
  102. (function() {
  103. var STORAGE_KEY = 'stocketbase_cart_backup';
  104. var serverItems = <?= json_encode($cartExportItems, JSON_UNESCAPED_SLASHES) ?>;
  105. ​
  106. try {
  107. if (serverItems.length > 0) {
  108. localStorage.setItem(STORAGE_KEY, JSON.stringify(serverItems));
  109. return;
  110. }
  111. ​
  112. var raw = localStorage.getItem(STORAGE_KEY);
  113. if (!raw) {
  114. return;
  115. }
  116. var backup = JSON.parse(raw);
  117. if (!Array.isArray(backup) || backup.length === 0) {
  118. return;
  119. }
  120. ​
  121. var csrfToken = <?= json_encode(Csrf::token()) ?>;
  122. var body = new URLSearchParams();
  123. body.set('action', 'restore_from_client');
  124. body.set('csrf_token', csrfToken);
  125. body.set('items', JSON.stringify(backup));
  126. ​
  127. fetch('cart.php', {
  128. method: 'POST',
  129. credentials: 'same-origin',
  130. headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  131. body: body.toString()
  132. }).then(function(response) {
  133. if (response.ok) {
  134. window.location.reload();
  135. }
  136. }).catch(function() {});
  137. } catch (e) {}
  138. })();
  139. </script>
  140. <?php endif; ?>
  141. <?= Icons::sprite() ?>
  142. <div class="site-shell">
  143. ​
  144. <?php $headerHasAuthButtons = UserAuth::check() || ($settings['login_enabled'] ?? '1') !== '0'; ?>
  145. <header class="site-header<?= $headerHasAuthButtons ? '' : ' site-header-no-auth' ?>">
  146. <div class="site-header-row">
  147. <?php
  148. $brandHasLogo = !empty($settings['site_logo_url']);
  149. $brandShowName = !$brandHasLogo || ($settings['site_show_name_with_logo'] ?? '0') === '1';
  150. $brandShortName = trim((string) ($settings['site_short_name'] ?? ''));
  151. $brandUseShort = $brandShowName && ($settings['site_short_name_enabled'] ?? '0') === '1' && $brandShortName !== '';
  152. ?>
  153. <a href="index.php" class="site-brand"<?= $brandUseShort ? ' data-short-name="1"' : '' ?>>
  154. <?php if ($brandHasLogo): ?>
  155. <img src="<?= htmlspecialchars($settings['site_logo_url']) ?>" alt="<?= htmlspecialchars($settings['site_name']) ?>" class="site-brand-logo">
  156. <?php endif; ?>
  157. <?php if ($brandShowName): ?>
  158. <span class="site-brand-full"><?= htmlspecialchars($settings['site_name']) ?></span>
  159. <?php if ($brandUseShort): ?>
  160. <span class="site-brand-short"><?= htmlspecialchars($brandShortName) ?></span>
  161. <?php endif; ?>
  162. <?php endif; ?>
  163. </a>
  164. ​
  165. <a href="cart.php" class="site-cart-link site-cart-link-header" id="site-cart-link-header">
  166. <?= renderShopIcon($settings) ?>
  167. <span class="site-cart-count"<?= $cartItemCount < 1 ? ' style="display:none;"' : '' ?>><?= (int) $cartItemCount ?></span>
  168. </a>
  169. ​
  170. <button type="button" class="site-menu-toggle" id="site-menu-toggle" aria-label="<?= htmlspecialchars(Language::get('nav_toggle_menu', 'Toggle menu')) ?>" aria-expanded="false">
  171. <?= Icons::icon('menu', 'icon icon-sm site-menu-toggle-icon-open') ?>
  172. <?= Icons::icon('x', 'icon icon-sm site-menu-toggle-icon-close') ?>
  173. </button>
  174. ​
  175. </div>
  176. ​
  177. <div class="site-nav-backdrop" id="site-nav-backdrop"></div>
  178. ​
  179. <nav class="site-nav" id="site-nav">
  180. <?php foreach ($navItems as $navItem): ?>
  181. <a href="<?= htmlspecialchars($navItem['url'], ENT_QUOTES) ?>" target="<?= htmlspecialchars($navItem['target'], ENT_QUOTES) ?>" <?= $navItem['target'] === '_blank' ? ' rel="noopener noreferrer"' : '' ?>><?= $navItem['icon_html'] ?? '' ?><?= htmlspecialchars($navItem['label']) ?></a>
  182. <?php endforeach; ?>
  183. <?= SiteFront::renderSearchControl('nav') ?>
  184. ​
  185. <div class="site-auth-links site-auth-links-mobile">
  186. <?php if (UserAuth::check()): ?>
  187. <a href="account.php"><?= htmlspecialchars(Language::get('nav_account', 'My account')) ?></a>
  188. <a href="user-login.php?logout=1"><?= htmlspecialchars(Language::get('nav_logout', 'Log out')) ?></a>
  189. <?php elseif (($settings['login_enabled'] ?? '1') !== '0'): ?>
  190. <a href="user-login.php?mode=login"><?= htmlspecialchars(Language::get('nav_login', 'Log in')) ?></a>
  191. <?php if (($settings['registration_enabled'] ?? '1') === '1'): ?>
  192. <a href="user-login.php?mode=register" class="site-auth-cta"><?= htmlspecialchars(Language::get('nav_signup', 'Sign up')) ?></a>
  193. <?php endif; ?>
  194. <?php endif; ?>
  195. </div>
  196. </nav>
  197. ​
  198. <div class="site-auth-links site-auth-links-desktop">
  199. <a href="cart.php" class="site-cart-link site-cart-link-desktop" id="site-cart-link-desktop">
  200. <?= renderShopIcon($settings, 'icon') ?>
  201. <span class="site-cart-count"<?= $cartItemCount < 1 ? ' style="display:none;"' : '' ?>><?= (int) $cartItemCount ?></span>
  202. </a>
  203. <?php if (UserAuth::check()): ?>
  204. <a href="account.php"><?= htmlspecialchars(Language::get('nav_account', 'My account')) ?></a>
  205. <a href="user-login.php?logout=1"><?= htmlspecialchars(Language::get('nav_logout', 'Log out')) ?></a>
  206. <?php elseif (($settings['login_enabled'] ?? '1') !== '0'): ?>
  207. <a href="user-login.php?mode=login"><?= htmlspecialchars(Language::get('nav_login', 'Log in')) ?></a>
  208. <?php if (($settings['registration_enabled'] ?? '1') === '1'): ?>
  209. <a href="user-login.php?mode=register" class="site-auth-cta"><?= htmlspecialchars(Language::get('nav_signup', 'Sign up')) ?></a>
  210. <?php endif; ?>
  211. <?php endif; ?>
  212. </div>
  213. </header>
  214. ​
  215. <main class="site-main">
  216. <?php foreach (SiteFront::activeAds('header') as $headerAd): ?>
  217. <?= SiteFront::renderAd($headerAd) ?>
  218. <?php endforeach; ?>