WebOrbiton
v3.0.0.0

FlatlyPage

351 lines · 14.3 KB
  1. <?php
  2. error_reporting(E_ALL);
  3. ini_set('display_errors', 0);
  4. ​
  5. header("X-Powered-By: FlatlyPage CMS");
  6. header("X-Content-Type-Options: nosniff");
  7. header("X-Frame-Options: SAMEORIGIN");
  8. header("X-XSS-Protection: 1; mode=block");
  9. header("Referrer-Policy: strict-origin-when-cross-origin");
  10. ​
  11. require_once __DIR__ . '/config.php';
  12. require_once __DIR__ . '/admin/publish-helpers.php';
  13. ​
  14. $slug = isset($_GET['slug']) ? sanitize($_GET['slug']) : '';
  15. ​
  16. define('CURRENT_PAGE', 'product');
  17. define('CURRENT_PAGE_SLUG', $slug);
  18. ​
  19. $lang_ok = flatly_init_front_lang();
  20. ​
  21. if (empty($slug)) {
  22. header('Location: ' . BASE_URL . flatly_lang_url(flatly_current_lang(), '/'));
  23. exit;
  24. }
  25. ​
  26. $page = null;
  27. $pages = $lang_ok ? get_all_products() : [];
  28. foreach ($pages as $p) {
  29. if ($p['slug'] === $slug) {
  30. $page = $p;
  31. break;
  32. }
  33. }
  34. ​
  35. if ($page && !is_page_published($page)) {
  36. $page = null;
  37. }
  38. ​
  39. if ($page) {
  40. $page_ref = flatly_split_lang_file_id((string) ($page['id'] ?? ('product-' . $slug)))[1];
  41. flatly_set_page_ref($page_ref);
  42. }
  43. ​
  44. $site_settings = get_site_settings();
  45. $page_title = $page['title'] ?? 'Error 404 - Page not found';
  46. $page_description = $page['description'] ?? '';
  47. $lang = isset($site_settings['site_language']) ? $site_settings['site_language'] : 'en';
  48. ​
  49. if ($page) {
  50. $page_index = $page['index'] ?? 'true';
  51. $page_follow = $page['follow'] ?? 'true';
  52. } else {
  53. $page_index = 'false';
  54. $page_follow = 'false';
  55. }
  56. ​
  57. $blocks = $page['blocks'] ?? [];
  58. $has_newsletter = false;
  59. ​
  60. foreach ($blocks as $block) {
  61. if (($block['type'] ?? '') === 'newsletter') {
  62. $has_newsletter = true;
  63. break;
  64. }
  65. }
  66. ​
  67. $nav_links = $site_settings['nav_links'] ?? [];
  68. ​
  69. $nav_buttons = $site_settings['nav_buttons'] ?? [];
  70. $footer = $site_settings['footer'] ?? [];
  71. $logo_text = $site_settings['logo_text'] ?? $site_settings['site_name'] ?? 'FlatlyPage';
  72. $logo_image = $site_settings['logo_image'] ?? '';
  73. ​
  74. $website_font = get_text_font($site_settings);
  75. ​
  76. $search_status_file = dirname(__FILE__) . '/data/search/search.txt';
  77. $show_search = false;
  78. ​
  79. if (file_exists($search_status_file)) {
  80. $status_content = trim(file_get_contents($search_status_file));
  81. if ($status_content === 'true') {
  82. $show_search = true;
  83. }
  84. }
  85. ?>
  86. <!-- Powered by FlatlyPage CMS -->
  87. <!DOCTYPE html>
  88. <html lang="<?php echo htmlspecialchars($lang); ?>" data-cms="flatlypage">
  89. ​
  90. <head>
  91. <meta charset="UTF-8">
  92. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  93. <title><?= e($page_title) ?> - <?= e($site_settings['site_name']) ?></title>
  94. <meta name="description" content="<?= e($page_description) ?>">
  95. <meta name="generator" content="FlatlyPage CMS">
  96. <?= flatly_canonical_link() ?>
  97. <?= flatly_hreflang_links() ?>
  98. <?= flatly_language_detect_script() ?>
  99. <meta name="robots" content="<?= ($page_index === 'true' ? 'index' : 'noindex') ?>, <?= ($page_follow === 'true' ? 'follow' : 'nofollow') ?>">
  100. <?php if ($page && !empty($page['publish_enabled']) && !empty($page['publish_at_utc'])): ?>
  101. <meta property="article:published_time" content="<?= e((new DateTime($page['publish_at_utc'], new DateTimeZone('UTC')))->setTimezone(new DateTimeZone($page['publish_timezone'] ?? 'Europe/Warsaw'))->format(DateTime::ATOM)) ?>">
  102. <?php endif; ?>
  103. <?php if (!empty($site_settings['favicon'])): ?>
  104. <link rel="icon" href="<?= e($site_settings['favicon']) ?>">
  105. <?php endif; ?>
  106. <?= font_head_html(site_all_fonts($site_settings), [get_site_interface_font($site_settings), $website_font]) ?>
  107. ​
  108. <link rel="stylesheet" href="/css/styles.css?v=<?= filemtime(__DIR__ . '/css/styles.css') ?>">
  109. ​
  110. <?= theme_link_html() ?>
  111. ​
  112. <style>
  113. <?= site_fonts_css($site_settings) ?>
  114. </style>
  115. ​
  116. <script type="application/ld+json">
  117. {
  118. "@context": "https://schema.org",
  119. "@type": "WebSite",
  120. "name": "<?= e($site_settings['site_name'] ?? SITE_NAME) ?>",
  121. "url": "<?= e(SITE_URL) ?>",
  122. "description": "<?= e($page_description) ?>",
  123. "publisher": {
  124. "@type": "Organization",
  125. "name": "<?= e($logo_text) ?>"
  126. }
  127. }
  128. </script>
  129. ​
  130. <?= flatly_custom_html('head') ?>
  131. </head>
  132. ​
  133. <body>
  134. <?= flatly_custom_html('body_start') ?>
  135. <nav class="navbar">
  136. <div class="container navbar-inner">
  137. <a href="<?= e(flatly_home_url()) ?>" class="logo">
  138. <?php if (!empty($logo_image)): ?>
  139. <img src="<?= e($logo_image) ?>" alt="<?= e($logo_text) ?>">
  140. <?php endif; ?>
  141. <?= e($logo_text) ?>
  142. </a>
  143. <?php if (!empty($nav_links)): ?>
  144. <div class="nav-links">
  145. <?php foreach ($nav_links as $link): ?>
  146. <a href="<?= e($link['url'] ?? '#') ?>"><?= nav_icon_html($link['icon'] ?? '') ?><?= e($link['label'] ?? '') ?></a>
  147. <?php endforeach; ?>
  148. </div>
  149. <?php endif; ?>
  150. ​
  151. <?php if (!empty($nav_buttons) || $show_search): ?>
  152. <div class="nav-buttons">
  153. <?php if ($show_search): ?>
  154. <a href="/search.php" class="btn btn-search" title="Search" style="padding: 8px; display: inline-flex; align-items: center; margin-right: 5px;">
  155. <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
  156. <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
  157. </svg>
  158. </a>
  159. <?php endif; ?>
  160. ​
  161. <?php foreach ($nav_buttons as $btn): ?>
  162. <a href="<?= e($btn['url'] ?? '#') ?>" class="btn btn-<?= e($btn['style'] ?? 'ghost') ?>"><?= nav_icon_html($btn['icon'] ?? '') ?><?= e($btn['label'] ?? '') ?></a>
  163. <?php endforeach; ?>
  164. </div>
  165. <?php endif; ?>
  166. ​
  167. <?= flatly_custom_html('nav') ?>
  168. <button class="mobile-menu-btn" aria-label="Menu" onclick="document.querySelector('.mobile-nav').classList.toggle('active')">
  169. <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
  170. <path d="M4 6h16M4 12h16M4 18h16" />
  171. </svg>
  172. </button>
  173. </div>
  174. </nav>
  175. ​
  176. <div class="mobile-nav">
  177. <?php if (!empty($nav_links)): ?>
  178. <?php foreach ($nav_links as $link): ?>
  179. <a href="<?= e($link['url'] ?? '#') ?>"><?= nav_icon_html($link['icon'] ?? '') ?><?= e($link['label'] ?? '') ?></a>
  180. <?php endforeach; ?>
  181. <?php endif; ?>
  182. ​
  183. <?php if (!empty($nav_buttons)): ?>
  184. <?php foreach ($nav_buttons as $btn): ?>
  185. <a href="<?= e($btn['url'] ?? '#') ?>" class="btn btn-<?= e($btn['style'] ?? 'ghost') ?>"><?= nav_icon_html($btn['icon'] ?? '') ?><?= e($btn['label'] ?? '') ?></a>
  186. <?php endforeach; ?>
  187. <?php endif; ?>
  188. ​
  189. <?php if ($show_search): ?>
  190. <a href="/search.php" class="btn btn-search" title="Search" style="padding: 8px; display: inline-flex; align-items: center; margin-right: 5px;">
  191. <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
  192. <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
  193. </svg>
  194. </a>
  195. <?php endif; ?>
  196. </div>
  197. ​
  198. <main>
  199. <?php if (!$page): ?>
  200. <section class="hero">
  201. <div class="hero-grid"></div>
  202. <div class="container hero-content" style="padding: 40px 0; text-align: center;">
  203. <h1 style="font-size: 3rem; color: var(--muted);">404</h1>
  204. <p style="font-size: 1.5rem; margin: 16px 0;">Page not found</p>
  205. <p style="color: var(--muted); margin-bottom: 32px;">Sorry, we couldn't find the page you're looking for.</p>
  206. <a href="<?= e(flatly_home_url()) ?>" class="btn btn-primary">Go back home</a>
  207. </div>
  208. </section>
  209. ​
  210. <?php else: ?>
  211. <?php $publish_display = format_publish_datetime($page, false); ?>
  212. <?php if ($publish_display): ?>
  213. <div class="container" style="padding-top: 24px;">
  214. <p class="page-publish-date" style="color: var(--muted); font-size: 0.875rem;">
  215. Published: <?= e($publish_display) ?>
  216. </p>
  217. </div>
  218. <?php endif; ?>
  219. <?php if (!empty($blocks)): ?>
  220. <?php foreach ($blocks as $block):
  221. echo apply_bbcode(render_block($block));
  222. endforeach; ?>
  223. <?php endif; ?>
  224. <?php endif; ?>
  225. ​
  226. </main>
  227. ​
  228. <footer class="footer">
  229. <div class="container">
  230. <div class="footer-grid">
  231. <div class="footer-brand">
  232. <h3>
  233. <?php if (!empty($logo_image)): ?>
  234. <img src="<?= e($logo_image) ?>" alt="<?= e($logo_text) ?>">
  235. <?php endif; ?>
  236. <?= e($logo_text) ?>
  237. </h3>
  238. <p><?= e($footer['brand_description'] ?? '') ?></p>
  239. <?php if (!empty($footer['social_links'])): ?>
  240. <div class="social-links">
  241. <?php foreach ($footer['social_links'] as $social): ?>
  242. <a href="<?= e($social['url'] ?? '#') ?>" aria-label="<?= e(ucfirst($social['platform'] ?? '')) ?>"><?= render_social_icon($social['platform'] ?? 'twitter') ?></a>
  243. <?php endforeach; ?>
  244. </div>
  245. <?php endif; ?>
  246. </div>
  247. <?php if (!empty($footer['columns'])): ?>
  248. <?php foreach ($footer['columns'] as $column): ?>
  249. <div class="footer-column">
  250. <h4><?= e($column['title'] ?? '') ?></h4>
  251. <?php if (!empty($column['links'])): ?>
  252. <ul>
  253. <?php foreach ($column['links'] as $link): ?>
  254. <li><a href="<?= e($link['url'] ?? '#') ?>"><?= e($link['label'] ?? '') ?></a></li>
  255. <?php endforeach; ?>
  256. </ul>
  257. <?php endif; ?>
  258. </div>
  259. <?php endforeach; ?>
  260. <?php endif; ?>
  261. </div>
  262. <div class="footer-bottom">
  263. <div style="flex: 1;">
  264. <p><?= e($footer['copyright'] ?? '© ' . date('Y') . ' ' . $logo_text . '. All rights reserved.') ?></p>
  265. <?php if (!empty($footer['bottom_links'])): ?>
  266. <div class="footer-legal">
  267. <?php foreach ($footer['bottom_links'] as $link): ?>
  268. <a href="<?= e($link['url'] ?? '#') ?>"><?= e($link['label'] ?? '') ?></a>
  269. <?php endforeach; ?>
  270. </div>
  271. <?php endif; ?>
  272. </div>
  273. ​
  274. <?= render_language_switcher() ?>
  275. ​
  276. <button class="theme-toggle" aria-label="Toggle theme" title="Toggle light/dark theme">
  277. <svg class="theme-icon-sun" viewBox="0 0 24 24" style="display: none;">
  278. <circle cx="12" cy="12" r="5" />
  279. <line x1="12" y1="1" x2="12" y2="3" />
  280. <line x1="12" y1="21" x2="12" y2="23" />
  281. <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
  282. <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
  283. <line x1="1" y1="12" x2="3" y2="12" />
  284. <line x1="21" y1="12" x2="23" y2="12" />
  285. <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
  286. <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
  287. </svg>
  288. <svg class="theme-icon-moon" viewBox="0 0 24 24" style="display: block;">
  289. <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
  290. </svg>
  291. </button>
  292. </div>
  293. </div>
  294. </footer>
  295. ​
  296. <script>
  297. const themeToggle = document.querySelector('.theme-toggle');
  298. const sunIcon = document.querySelector('.theme-icon-sun');
  299. const moonIcon = document.querySelector('.theme-icon-moon');
  300. const html = document.documentElement;
  301. ​
  302. function initTheme() {
  303. let theme = localStorage.getItem('theme');
  304. if (!theme) {
  305. const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  306. theme = prefersDark ? 'dark' : 'light';
  307. }
  308. setTheme(theme);
  309. }
  310. ​
  311. function setTheme(theme) {
  312. if (theme === 'light') {
  313. html.setAttribute('data-theme', 'light');
  314. sunIcon.style.display = 'block';
  315. moonIcon.style.display = 'none';
  316. localStorage.setItem('theme', 'light');
  317. } else {
  318. html.removeAttribute('data-theme');
  319. sunIcon.style.display = 'none';
  320. moonIcon.style.display = 'block';
  321. localStorage.setItem('theme', 'dark');
  322. }
  323. }
  324. ​
  325. function toggleTheme() {
  326. const currentTheme = html.getAttribute('data-theme') || 'dark';
  327. const newTheme = currentTheme === 'light' ? 'dark' : 'light';
  328. setTheme(newTheme);
  329. }
  330. ​
  331. if (themeToggle) {
  332. themeToggle.addEventListener('click', toggleTheme);
  333. }
  334. ​
  335. initTheme();
  336. ​
  337. document.querySelectorAll('.mobile-nav a').forEach(link => {
  338. link.addEventListener('click', () => {
  339. document.querySelector('.mobile-nav').classList.remove('active');
  340. });
  341. });
  342. </script>
  343. ​
  344. <?php if ($has_newsletter): ?>
  345. <script src="/newsletter/newsletter-form.js?v=<?= date('YmdH') ?>"></script>
  346. <?php endif; ?>
  347. <?= flatly_custom_html('body_end') ?>
  348. <?= flatly_custom_js() ?>
  349. </body>
  350. ​
  351. </html>