WebOrbiton
v3.0.0.2

FlatlyPage

425 lines · 18.9 KB
  1. <?php
  2. error_reporting(E_ALL);
  3. ini_set('display_errors', 0);
  4. ​
  5. header_remove('X-Powered-By');
  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. ​
  13. $slug = isset($_GET['slug']) ? sanitize($_GET['slug']) : 'contact';
  14. ​
  15. define('CURRENT_PAGE', 'contact');
  16. ​
  17. if (!flatly_init_front_lang()) {
  18. header('Location: ' . BASE_URL . '/');
  19. exit;
  20. }
  21. ​
  22. $site_settings = get_site_settings();
  23. if (empty($site_settings['contact_page_enabled']) || $site_settings['contact_page_enabled'] === false) {
  24. header('Location: ' . BASE_URL . '/');
  25. exit;
  26. }
  27. ​
  28. require_once __DIR__ . '/contact-handler.php';
  29. ​
  30. if (empty($slug)) {
  31. header('Location: ' . BASE_URL . flatly_lang_url(flatly_current_lang(), '/'));
  32. exit;
  33. }
  34. ​
  35. $page = null;
  36. $pages = get_all_products();
  37. foreach ($pages as $p) {
  38. if ($p['slug'] === $slug) {
  39. $page = $p;
  40. break;
  41. }
  42. }
  43. ​
  44. flatly_set_page_ref($page ? flatly_split_lang_file_id((string) ($page['id'] ?? ('product-' . $slug)))[1] : 'product-' . $slug);
  45. ​
  46. $site_settings = get_site_settings();
  47. $page_title = $page['title'] ?? 'Contact Us';
  48. $page_description = $page['description'] ?? '';
  49. $lang = isset($site_settings['site_language']) ? $site_settings['site_language'] : 'en';
  50. $blocks = $page['blocks'] ?? [];
  51. $has_newsletter = false;
  52. ​
  53. foreach ($blocks as $block) {
  54. if (($block['type'] ?? '') === 'newsletter') {
  55. $has_newsletter = true;
  56. break;
  57. }
  58. }
  59. ​
  60. $nav_links = $site_settings['nav_links'] ?? [];
  61. ​
  62. $nav_buttons = $site_settings['nav_buttons'] ?? [];
  63. $footer = $site_settings['footer'] ?? [];
  64. $logo_text = $site_settings['logo_text'] ?? $site_settings['site_name'] ?? 'FlatlyPage';
  65. $logo_image = $site_settings['logo_image'] ?? '';
  66. ​
  67. $website_font = get_text_font($site_settings);
  68. ​
  69. $search_status_file = dirname(__FILE__) . '/data/search/search.txt';
  70. $show_search = false;
  71. ​
  72. if (file_exists($search_status_file)) {
  73. $status_content = trim(file_get_contents($search_status_file));
  74. if ($status_content === 'true') {
  75. $show_search = true;
  76. }
  77. }
  78. ?>
  79. <!DOCTYPE html>
  80. <html lang="<?php echo htmlspecialchars($lang); ?>">
  81. ​
  82. <head>
  83. <meta charset="UTF-8">
  84. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  85. <title><?= e($page_title) ?> - <?= e($site_settings['site_name']) ?></title>
  86. <meta name="description" content="<?= e($page_description) ?>">
  87. <?= flatly_canonical_link() ?>
  88. <?= flatly_hreflang_links() ?>
  89. <?= flatly_language_detect_script() ?>
  90. <?php if (!empty($site_settings['favicon'])): ?>
  91. <link rel="icon" href="<?= e($site_settings['favicon']) ?>">
  92. <?php endif; ?>
  93. <?= font_head_html(site_all_fonts($site_settings), [get_site_interface_font($site_settings), $website_font]) ?>
  94. ​
  95. <link rel="stylesheet" href="/css/contact.css?v=<?= filemtime(__DIR__ . '/css/contact.css') ?>">
  96. ​
  97. <?= theme_link_html() ?>
  98. ​
  99. <link rel="stylesheet" href="/css/styles.css?v=<?= filemtime(__DIR__ . '/css/styles.css') ?>">
  100. ​
  101. <style>
  102. <?= site_fonts_css($site_settings) ?>
  103. </style>
  104. ​
  105. <script type="application/ld+json">
  106. {
  107. "@context": "https://schema.org",
  108. "@type": "WebSite",
  109. "name": "<?= e($site_settings['site_name'] ?? SITE_NAME) ?>",
  110. "url": "<?= e(SITE_URL) ?>",
  111. "description": "<?= e($page_description) ?>",
  112. "publisher": {
  113. "@type": "Organization",
  114. "name": "<?= e($logo_text) ?>"
  115. }
  116. }
  117. </script>
  118. ​
  119. <?= flatly_custom_html('head') ?>
  120. </head>
  121. ​
  122. <body>
  123. <?= flatly_custom_html('body_start') ?>
  124. <nav class="navbar">
  125. <div class="container navbar-inner">
  126. <a href="<?= e(flatly_home_url()) ?>" class="logo">
  127. <?php if (!empty($logo_image)): ?>
  128. <img src="<?= e($logo_image) ?>" alt="<?= e($logo_text) ?>">
  129. <?php endif; ?>
  130. <?= e($logo_text) ?>
  131. </a>
  132. <?php if (!empty($nav_links)): ?>
  133. <div class="nav-links">
  134. <?php foreach ($nav_links as $link): ?>
  135. <a href="<?= e($link['url'] ?? '#') ?>"><?= nav_icon_html($link['icon'] ?? '') ?><?= e($link['label'] ?? '') ?></a>
  136. <?php endforeach; ?>
  137. </div>
  138. <?php endif; ?>
  139. <?php if (!empty($nav_buttons) || $show_search): ?>
  140. <div class="nav-buttons">
  141. <?php if ($show_search): ?>
  142. <a href="/search.php" class="btn btn-search" title="Search" style="padding: 8px; display: inline-flex; align-items: center; margin-right: 5px;">
  143. <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
  144. <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
  145. </svg>
  146. </a>
  147. <?php endif; ?>
  148. ​
  149. <?php foreach ($nav_buttons as $btn): ?>
  150. <a href="<?= e($btn['url'] ?? '#') ?>" class="btn btn-<?= e($btn['style'] ?? 'ghost') ?>"><?= nav_icon_html($btn['icon'] ?? '') ?><?= e($btn['label'] ?? '') ?></a>
  151. <?php endforeach; ?>
  152. </div>
  153. <?php endif; ?>
  154. ​
  155. <?= flatly_custom_html('nav') ?>
  156. <button class="mobile-menu-btn" aria-label="Menu" onclick="document.querySelector('.mobile-nav').classList.toggle('active')">
  157. <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
  158. <path d="M4 6h16M4 12h16M4 18h16" />
  159. </svg>
  160. </button>
  161. </div>
  162. </nav>
  163. ​
  164. <div class="mobile-nav">
  165. <?php if (!empty($nav_links)): ?>
  166. <?php foreach ($nav_links as $link): ?>
  167. <a href="<?= e($link['url'] ?? '#') ?>"><?= nav_icon_html($link['icon'] ?? '') ?><?= e($link['label'] ?? '') ?></a>
  168. <?php endforeach; ?>
  169. <?php endif; ?>
  170. <?php if (!empty($nav_buttons)): ?>
  171. <?php foreach ($nav_buttons as $btn): ?>
  172. <a href="<?= e($btn['url'] ?? '#') ?>" class="btn btn-<?= e($btn['style'] ?? 'ghost') ?>"><?= nav_icon_html($btn['icon'] ?? '') ?><?= e($btn['label'] ?? '') ?></a>
  173. <?php endforeach; ?>
  174. <?php endif; ?>
  175. ​
  176. <?php if ($show_search): ?>
  177. <a href="/search.php" class="btn btn-search" title="Search" style="padding: 8px; display: inline-flex; align-items: center; margin-right: 5px;">
  178. <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
  179. <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
  180. </svg>
  181. </a>
  182. <?php endif; ?>
  183. </div>
  184. ​
  185. <main>
  186. <?php if (!empty($blocks)): ?>
  187. <?php foreach ($blocks as $block):
  188. echo apply_bbcode(render_block($block));
  189. endforeach; ?>
  190. <?php endif; ?>
  191. ​
  192. <section class="contact-form-section" id="contact-form">
  193. <div class="container">
  194. <div class="section-header">
  195. <h2>Contact Us</h2>
  196. <p>Have questions? Reach out to us!</p>
  197. </div>
  198. ​
  199. <?php $message = get_contact_message(); ?>
  200. <?php if ($message): ?>
  201. <div style="padding: 1rem; margin-bottom: 1.5rem; border-radius: 8px; <?= $message['type'] === 'success' ? 'background: #dcfce7; color: #166534;' : 'background: #fee2e2; color: #991b1b;' ?>">
  202. <?= e($message['text']) ?>
  203. </div>
  204. <?php endif; ?>
  205. ​
  206. <div class="contact-form-wrapper">
  207. <form class="contact-form" method="POST" action="">
  208. <input type="text" name="website" value="" style="display:none !important; visibility:hidden; position:absolute; left:-9999px;" tabindex="-1" autocomplete="off">
  209. <input type="hidden" name="_lt" value="<?= time() ?>">
  210. <input type="hidden" name="_token" value="<?= e($_SESSION['form_token_raw'] ?? '') ?>">
  211. ​
  212. <div class="form-group">
  213. <label for="name">Name <span style="color: #ef4444;">*</span></label>
  214. <input type="text" id="name" name="name" required maxlength="100"
  215. placeholder="John Doe" value="<?= e(get_contact_data('name')) ?>">
  216. <?php if ($error = get_contact_error('name')): ?>
  217. <small style="color: #ef4444;"><?= e($error) ?></small>
  218. <?php endif; ?>
  219. </div>
  220. ​
  221. <div class="form-group">
  222. <label for="email">Email <span style="color: #ef4444;">*</span></label>
  223. <input type="email" id="email" name="email" required
  224. placeholder="john@example.com" value="<?= e(get_contact_data('email')) ?>">
  225. <?php if ($error = get_contact_error('email')): ?>
  226. <small style="color: #ef4444;"><?= e($error) ?></small>
  227. <?php endif; ?>
  228. </div>
  229. ​
  230. <div class="form-group">
  231. <label for="message">Message <span style="color: #ef4444;">*</span></label>
  232. <textarea id="message" name="message" rows="5" required maxlength="5000"
  233. placeholder="Your message..."><?= e(get_contact_data('message')) ?></textarea>
  234. <?php if ($error = get_contact_error('message')): ?>
  235. <small style="color: #ef4444;"><?= e($error) ?></small>
  236. <?php endif; ?>
  237. <small style="color: var(--muted); font-size: 0.875rem; margin-top: 4px; display: block;">
  238. Maximum 5000 characters
  239. </small>
  240. </div>
  241. ​
  242. <div class="form-group">
  243. <label>Security check <span style="color: #ef4444;">*</span></label>
  244. <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap;">
  245. <img id="antibot-img"
  246. src="/antibot.php?<?= time() ?>"
  247. alt="ANTIBOT"
  248. width="200" height="70"
  249. style="border: 1px solid var(--border, #e2e8f0); border-radius: 6px; display: block; cursor: pointer;"
  250. title="Click to refresh">
  251. <button type="button"
  252. onclick="document.getElementById('antibot-img').src='<?= BASE_URL ?>/antibot.php?' + Date.now()"
  253. style="background: none; border: 1px solid var(--border, #e2e8f0); border-radius: 6px; padding: 6px 10px; cursor: pointer; font-size: 0.8rem; color: var(--muted);"
  254. title="Refresh ANTIBOT">
  255. ↻ Refresh
  256. </button>
  257. </div>
  258. <input type="text" id="antibot" name="antibot" required
  259. maxlength="6" autocomplete="off" spellcheck="false"
  260. placeholder="Enter the 6 characters shown above"
  261. style="text-transform: uppercase; letter-spacing: 0.15em;">
  262. <small style="color: var(--muted); font-size: 0.8rem; margin-top: 4px; display: block;">
  263. Type the characters from the image (not case sensitive). Click the image to refresh.
  264. </small>
  265. <?php if ($error = get_contact_error('antibot')): ?>
  266. <small style="color: #ef4444;"><?= e($error) ?></small>
  267. <?php endif; ?>
  268. </div>
  269. ​
  270. <button type="submit" class="btn btn-primary">
  271. Send Message
  272. </button>
  273. ​
  274. <p style="margin-top: 12px; font-size: 0.875rem; color: var(--muted);">
  275. <span style="color: #ef4444;">*</span> Required fields
  276. </p>
  277. </form>
  278. </div>
  279. ​
  280. <script>
  281. (function() {
  282. var img = document.getElementById('antibot-img');
  283. if (img) {
  284. img.addEventListener('click', function() {
  285. img.src = '<?= BASE_URL ?>/antibot.php?' + Date.now();
  286. });
  287. }
  288. var input = document.getElementById('antibot');
  289. if (input) {
  290. input.addEventListener('input', function() {
  291. this.value = this.value.toUpperCase();
  292. });
  293. }
  294. })();
  295. </script>
  296. ​
  297. </div>
  298. </section>
  299. ​
  300. </main>
  301. ​
  302. <footer class="footer">
  303. <div class="container">
  304. <div class="footer-grid">
  305. <div class="footer-brand">
  306. <h3>
  307. <?php if (!empty($logo_image)): ?>
  308. <img src="<?= e($logo_image) ?>" alt="<?= e($logo_text) ?>">
  309. <?php endif; ?>
  310. <?= e($logo_text) ?>
  311. </h3>
  312. <p><?= e($footer['brand_description'] ?? '') ?></p>
  313. <?php if (!empty($footer['social_links'])): ?>
  314. <div class="social-links">
  315. <?php foreach ($footer['social_links'] as $social): ?>
  316. <a href="<?= e($social['url'] ?? '#') ?>" aria-label="<?= e(ucfirst($social['platform'] ?? '')) ?>"><?= render_social_icon($social['platform'] ?? 'twitter') ?></a>
  317. <?php endforeach; ?>
  318. </div>
  319. <?php endif; ?>
  320. </div>
  321. <?php if (!empty($footer['columns'])): ?>
  322. <?php foreach ($footer['columns'] as $column): ?>
  323. <div class="footer-column">
  324. <h4><?= e($column['title'] ?? '') ?></h4>
  325. <?php if (!empty($column['links'])): ?>
  326. <ul>
  327. <?php foreach ($column['links'] as $link): ?>
  328. <li><a href="<?= e($link['url'] ?? '#') ?>"><?= e($link['label'] ?? '') ?></a></li>
  329. <?php endforeach; ?>
  330. </ul>
  331. <?php endif; ?>
  332. </div>
  333. <?php endforeach; ?>
  334. <?php endif; ?>
  335. </div>
  336. <div class="footer-bottom">
  337. <div style="flex: 1;">
  338. <p><?= e($footer['copyright'] ?? '© ' . date('Y') . ' ' . $logo_text . '. All rights reserved.') ?></p>
  339. <?php if (!empty($footer['bottom_links'])): ?>
  340. <div class="footer-legal">
  341. <?php foreach ($footer['bottom_links'] as $link): ?>
  342. <a href="<?= e($link['url'] ?? '#') ?>"><?= e($link['label'] ?? '') ?></a>
  343. <?php endforeach; ?>
  344. </div>
  345. <?php endif; ?>
  346. </div>
  347. ​
  348. <?= render_language_switcher() ?>
  349. ​
  350. <button class="theme-toggle" aria-label="Toggle theme" title="Toggle light/dark theme">
  351. <svg class="theme-icon-sun" viewBox="0 0 24 24" style="display: none;">
  352. <circle cx="12" cy="12" r="5" />
  353. <line x1="12" y1="1" x2="12" y2="3" />
  354. <line x1="12" y1="21" x2="12" y2="23" />
  355. <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
  356. <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
  357. <line x1="1" y1="12" x2="3" y2="12" />
  358. <line x1="21" y1="12" x2="23" y2="12" />
  359. <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
  360. <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
  361. </svg>
  362. <svg class="theme-icon-moon" viewBox="0 0 24 24" style="display: block;">
  363. <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
  364. </svg>
  365. </button>
  366. </div>
  367. </div>
  368. </footer>
  369. ​
  370. <script>
  371. const themeToggle = document.querySelector('.theme-toggle');
  372. const sunIcon = document.querySelector('.theme-icon-sun');
  373. const moonIcon = document.querySelector('.theme-icon-moon');
  374. const html = document.documentElement;
  375. ​
  376. function initTheme() {
  377. let theme = localStorage.getItem('theme');
  378. if (!theme) {
  379. const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  380. theme = prefersDark ? 'dark' : 'light';
  381. }
  382. setTheme(theme);
  383. }
  384. ​
  385. function setTheme(theme) {
  386. if (theme === 'light') {
  387. html.setAttribute('data-theme', 'light');
  388. sunIcon.style.display = 'block';
  389. moonIcon.style.display = 'none';
  390. localStorage.setItem('theme', 'light');
  391. } else {
  392. html.removeAttribute('data-theme');
  393. sunIcon.style.display = 'none';
  394. moonIcon.style.display = 'block';
  395. localStorage.setItem('theme', 'dark');
  396. }
  397. }
  398. ​
  399. function toggleTheme() {
  400. const currentTheme = html.getAttribute('data-theme') || 'dark';
  401. const newTheme = currentTheme === 'light' ? 'dark' : 'light';
  402. setTheme(newTheme);
  403. }
  404. ​
  405. if (themeToggle) {
  406. themeToggle.addEventListener('click', toggleTheme);
  407. }
  408. ​
  409. initTheme();
  410. ​
  411. document.querySelectorAll('.mobile-nav a').forEach(link => {
  412. link.addEventListener('click', () => {
  413. document.querySelector('.mobile-nav').classList.remove('active');
  414. });
  415. });
  416. </script>
  417. ​
  418. <?php if ($has_newsletter): ?>
  419. <script src="/newsletter/newsletter-form.js"></script>
  420. <?php endif; ?>
  421. <?= flatly_custom_html('body_end') ?>
  422. <?= flatly_custom_js() ?>
  423. </body>
  424. ​
  425. </html>