WebOrbiton
v3.0.0.0

FlatlyPage

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