v3.0.0.0
FlatlyPage
- <?php
- error_reporting(E_ALL);
- ini_set('display_errors', 0);
-
- header("X-Powered-By: FlatlyPage CMS");
- header("X-Content-Type-Options: nosniff");
- header("X-Frame-Options: SAMEORIGIN");
- header("X-XSS-Protection: 1; mode=block");
- header("Referrer-Policy: strict-origin-when-cross-origin");
-
- require_once __DIR__ . '/config.php';
- require_once __DIR__ . '/admin/publish-helpers.php';
-
- if (!flatly_init_front_lang()) {
- $_GET['slug'] = (string) $_GET['lang'];
- unset($_GET['lang']);
- require __DIR__ . '/page.php';
- exit;
- }
-
- define('CURRENT_PAGE', 'home');
- flatly_set_page_ref('index');
-
- $page_data = load_page_lang('index', flatly_current_lang()) ?? load_page('index');
- if (!$page_data) {
- $page_data = ['title' => 'Welcome', 'description' => 'Welcome to FlatlyPage', 'blocks' => []];
- }
-
- $site_settings = get_site_settings();
-
- $page_title = $page_data['title'] ?? $site_settings['site_name'] ?? SITE_NAME;
- $page_description = $page_data['description'] ?? $site_settings['site_description'] ?? '';
- $lang = isset($site_settings['site_language']) ? $site_settings['site_language'] : 'en';
- $blocks = $page_data['blocks'] ?? [];
- $has_newsletter = false;
-
- foreach ($blocks as $block) {
- if (($block['type'] ?? '') === 'newsletter') {
- $has_newsletter = true;
- break;
- }
- }
-
- $nav_links = $site_settings['nav_links'] ?? [];
- $nav_buttons = $site_settings['nav_buttons'] ?? [];
- $footer = $site_settings['footer'] ?? [];
- $logo_text = $site_settings['logo_text'] ?? $site_settings['site_name'] ?? SITE_NAME;
- $logo_image = $site_settings['logo_image'] ?? '';
-
- $website_font = get_text_font($site_settings);
-
- $search_status_file = dirname(__FILE__) . '/data/search/search.txt';
- $show_search = false;
-
- if (file_exists($search_status_file)) {
- $status_content = trim(file_get_contents($search_status_file));
- if ($status_content === 'true') {
- $show_search = true;
- }
- }
- ?>
- <!-- Powered by FlatlyPage CMS -->
- <!DOCTYPE html>
- <html lang="<?php echo htmlspecialchars($lang); ?>" data-cms="flatlypage">
-
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title><?= e($page_title) ?></title>
- <meta name="description" content="<?= e($page_description) ?>">
- <meta name="generator" content="FlatlyPage CMS">
- <?= flatly_canonical_link() ?>
- <?= flatly_hreflang_links() ?>
- <?= flatly_language_detect_script() ?>
- <?php if (!empty($site_settings['favicon'])): ?>
- <link rel="icon" href="<?= e($site_settings['favicon']) ?>">
- <?php endif; ?>
- <?= font_head_html(site_all_fonts($site_settings), [get_site_interface_font($site_settings), $website_font]) ?>
-
- <link rel="stylesheet" href="/css/styles.css?v=<?= filemtime(__DIR__ . '/css/styles.css') ?>">
-
- <?= theme_link_html() ?>
-
- <style>
- <?= site_fonts_css($site_settings) ?>
- </style>
-
- <script type="application/ld+json">
- {
- "@context": "https://schema.org",
- "@type": "WebSite",
- "name": "<?= e($site_settings['site_name'] ?? SITE_NAME) ?>",
- "url": "<?= e(SITE_URL) ?>",
- "description": "<?= e($site_settings['site_description'] ?? '') ?>",
- "publisher": {
- "@type": "Organization",
- "name": "<?= e($logo_text) ?>"
- }
- }
- </script>
-
- <?= flatly_custom_html('head') ?>
- </head>
-
- <body>
- <?= flatly_custom_html('body_start') ?>
- <nav class="navbar">
- <div class="container navbar-inner">
- <a href="<?= e(flatly_home_url()) ?>" class="logo">
- <?php if (!empty($logo_image)): ?>
- <img src="<?= e($logo_image) ?>" alt="<?= e($logo_text) ?>">
- <?php endif; ?>
- <?= e($logo_text) ?>
- </a>
- <?php if (!empty($nav_links)): ?>
- <div class="nav-links">
- <?php foreach ($nav_links as $link): ?>
- <a href="<?= e($link['url'] ?? '#') ?>"><?= nav_icon_html($link['icon'] ?? '') ?><?= e($link['label'] ?? '') ?></a>
- <?php endforeach; ?>
- </div>
- <?php endif; ?>
- <?php if (!empty($nav_buttons) || $show_search): ?>
- <div class="nav-buttons">
- <?php if ($show_search): ?>
- <a href="/search.php" class="btn btn-search" title="Search" style="padding: 8px; display: inline-flex; align-items: center; margin-right: 5px;">
- <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
- <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
- </svg>
- </a>
- <?php endif; ?>
-
- <?php foreach ($nav_buttons as $btn): ?>
- <a href="<?= e($btn['url'] ?? '#') ?>" class="btn btn-<?= e($btn['style'] ?? 'ghost') ?>"><?= nav_icon_html($btn['icon'] ?? '') ?><?= e($btn['label'] ?? '') ?></a>
- <?php endforeach; ?>
- </div>
- <?php endif; ?>
- <?= flatly_custom_html('nav') ?>
- <button class="mobile-menu-btn" aria-label="Menu" onclick="document.querySelector('.mobile-nav').classList.toggle('active')">
- <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M4 6h16M4 12h16M4 18h16" />
- </svg>
- </button>
- </div>
- </nav>
- <div class="mobile-nav">
- <?php if (!empty($nav_links)): ?>
- <?php foreach ($nav_links as $link): ?>
- <a href="<?= e($link['url'] ?? '#') ?>"><?= nav_icon_html($link['icon'] ?? '') ?><?= e($link['label'] ?? '') ?></a>
- <?php endforeach; ?>
- <?php endif; ?>
-
- <?php if (!empty($nav_buttons)): ?>
- <?php foreach ($nav_buttons as $btn): ?>
- <a href="<?= e($btn['url'] ?? '#') ?>" class="btn btn-<?= e($btn['style'] ?? 'ghost') ?>"><?= nav_icon_html($btn['icon'] ?? '') ?><?= e($btn['label'] ?? '') ?></a>
- <?php endforeach; ?>
- <?php endif; ?>
-
- <?php if ($show_search): ?>
- <a href="/search.php" class="btn btn-search" title="Search" style="padding: 8px; display: inline-flex; align-items: center; margin-right: 5px;">
- <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
- <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
- </svg>
- </a>
- <?php endif; ?>
- </div>
-
- <main>
- <?php if (!empty($blocks)): ?>
- <?php foreach ($blocks as $block):
- echo apply_bbcode(render_block($block));
- endforeach; ?>
- <?php endif; ?>
-
- <?php
- $site_settings = get_site_settings();
- $limit = 6;
- $cache_time = 60;
-
- if ($site_settings['recent_pages_enabled'] ?? false):
- $all_pages = get_all_products();
-
- $all_pages = array_filter($all_pages, function ($page) {
- $slug_valid = ($page['slug'] ?? '') !== 'contact';
- $show_in_latest = $page['show_in_latest'] ?? false;
- return $slug_valid && $show_in_latest && is_page_published($page);
- });
-
- usort($all_pages, function ($a, $b) {
- $timeA = !empty($a['publish_enabled']) && !empty($a['publish_at_utc'])
- ? strtotime($a['publish_at_utc'] . ' UTC')
- : strtotime($a['created_at'] ?? $a['updated_at'] ?? 'now');
- $timeB = !empty($b['publish_enabled']) && !empty($b['publish_at_utc'])
- ? strtotime($b['publish_at_utc'] . ' UTC')
- : strtotime($b['created_at'] ?? $b['updated_at'] ?? 'now');
- return $timeB - $timeA;
- });
- $recent_pages = array_slice($all_pages, 0, $limit);
-
- $cache_key = md5(serialize(array_map(function ($page) {
- $publish_marker = !empty($page['publish_enabled']) && !empty($page['publish_at_utc'])
- ? $page['publish_at_utc']
- : ($page['created_at'] ?? $page['updated_at'] ?? 'now');
- return $publish_marker;
- }, $recent_pages)));
- $cache_file = __DIR__ . "/cache/pages_grid" . (flatly_current_lang() !== null ? '-' . flatly_current_lang() : '') . ".html";
-
- if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time)) {
- include($cache_file);
- } else {
- ob_start();
- ?>
- <section class="pages-section" style="margin-bottom: 50px;">
- <div class="container">
- <h2 style="font-size: 2.5rem; margin-bottom: 40px;">Latest content</h2>
- <?php if (empty($recent_pages)): ?>
- <div style="text-align: center; padding: 40px; color: var(--text-secondary);">
- <p>No pages found.</p>
- </div>
- <?php else: ?>
- <div class="pages-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px;">
- <?php foreach ($recent_pages as $page): ?>
- <article class="pages-card" style="background: var(--card); border: 1px solid var(--border); padding: 24px; border-radius: 12px; display: flex; flex-direction: column;">
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
- <span style="color: var(--primary-color); font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 1px;">
- <?= e($page['blocks'][0]['data']['badge'] ?? '') ?>
- </span>
- <time style="color: var(--text-secondary); font-size: 0.7rem;">
- <?php
- $card_date = !empty($page['publish_enabled']) && !empty($page['publish_at_utc'])
- ? get_publish_date_only($page)
- : date('d.m.Y', strtotime($page['created_at'] ?? $page['updated_at'] ?? 'now'));
- echo e($card_date);
- ?>
- </time>
- </div>
- <h3 style="margin: 0 0 12px 0; font-size: 1.4rem; color: var(--text);">
- <?= e($page['title'] ?? 'No title') ?>
- </h3>
- <p style="color: var(--text-secondary); line-height: 1.6; font-size: 0.95rem; flex-grow: 1;">
- <?= e(mb_strimwidth($page['description'] ?? '', 0, 120, "...")) ?>
- </p>
- <a href="<?= e(flatly_lang_url(flatly_current_lang(), '/' . ($page['slug'] ?? ''))) ?>" style="display: inline-block; margin-top: 20px; color: var(--primary-color); text-decoration: none; font-weight: 600; font-size: 0.9rem;">
- Learn more <span style="margin-left: 5px;">→</span>
- </a>
- </article>
- <?php endforeach; ?>
- </div>
- <?php endif; ?>
- </div>
- </section>
- <?php
- $output = ob_get_clean();
- if (!is_dir(__DIR__ . '/cache')) {
- mkdir(__DIR__ . '/cache', 0755, true);
- }
- file_put_contents($cache_file, $output);
- echo $output;
- }
- endif;
- ?>
-
- </main>
-
- <footer class="footer">
- <div class="container">
- <div class="footer-grid">
- <div class="footer-brand">
- <h3>
- <?php if (!empty($logo_image)): ?>
- <img src="<?= e($logo_image) ?>" alt="<?= e($logo_text) ?>">
- <?php endif; ?>
- <?= e($logo_text) ?>
- </h3>
- <p><?= e($footer['brand_description'] ?? '') ?></p>
- <?php if (!empty($footer['social_links'])): ?>
- <div class="social-links">
- <?php foreach ($footer['social_links'] as $social): ?>
- <a href="<?= e($social['url'] ?? '#') ?>" aria-label="<?= e(ucfirst($social['platform'] ?? '')) ?>"><?= render_social_icon($social['platform'] ?? 'twitter') ?></a>
- <?php endforeach; ?>
- </div>
- <?php endif; ?>
- </div>
- <?php if (!empty($footer['columns'])): ?>
- <?php foreach ($footer['columns'] as $column): ?>
- <div class="footer-column">
- <h4><?= e($column['title'] ?? '') ?></h4>
- <?php if (!empty($column['links'])): ?>
- <ul>
- <?php foreach ($column['links'] as $link): ?>
- <li><a href="<?= e($link['url'] ?? '#') ?>"><?= e($link['label'] ?? '') ?></a></li>
- <?php endforeach; ?>
- </ul>
- <?php endif; ?>
- </div>
- <?php endforeach; ?>
- <?php endif; ?>
- </div>
- <div class="footer-bottom">
- <div style="flex: 1;">
- <p><?= e($footer['copyright'] ?? '© ' . date('Y') . ' ' . $logo_text . '. All rights reserved.') ?></p>
- <?php if (!empty($footer['bottom_links'])): ?>
- <div class="footer-legal">
- <?php foreach ($footer['bottom_links'] as $link): ?>
- <a href="<?= e($link['url'] ?? '#') ?>"><?= e($link['label'] ?? '') ?></a>
- <?php endforeach; ?>
- </div>
- <?php endif; ?>
- </div>
-
-
- <?= render_language_switcher() ?>
-
- <button class="theme-toggle" aria-label="Toggle theme" title="Toggle light/dark theme">
- <svg class="theme-icon-sun" viewBox="0 0 24 24" style="display: none;">
- <circle cx="12" cy="12" r="5" />
- <line x1="12" y1="1" x2="12" y2="3" />
- <line x1="12" y1="21" x2="12" y2="23" />
- <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
- <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
- <line x1="1" y1="12" x2="3" y2="12" />
- <line x1="21" y1="12" x2="23" y2="12" />
- <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
- <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
- </svg>
- <svg class="theme-icon-moon" viewBox="0 0 24 24" style="display: block;">
- <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
- </svg>
- </button>
- </div>
- </div>
- </footer>
-
- <script>
- const themeToggle = document.querySelector('.theme-toggle');
- const sunIcon = document.querySelector('.theme-icon-sun');
- const moonIcon = document.querySelector('.theme-icon-moon');
- const html = document.documentElement;
-
- function initTheme() {
- let theme = localStorage.getItem('theme');
- if (!theme) {
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
- theme = prefersDark ? 'dark' : 'light';
- }
- setTheme(theme);
- }
-
- function setTheme(theme) {
- if (theme === 'light') {
- html.setAttribute('data-theme', 'light');
- sunIcon.style.display = 'block';
- moonIcon.style.display = 'none';
- localStorage.setItem('theme', 'light');
- } else {
- html.removeAttribute('data-theme');
- sunIcon.style.display = 'none';
- moonIcon.style.display = 'block';
- localStorage.setItem('theme', 'dark');
- }
- }
-
- function toggleTheme() {
- const currentTheme = html.getAttribute('data-theme') || 'dark';
- const newTheme = currentTheme === 'light' ? 'dark' : 'light';
- setTheme(newTheme);
- }
-
- if (themeToggle) {
- themeToggle.addEventListener('click', toggleTheme);
- }
-
- initTheme();
-
- document.querySelectorAll('.mobile-nav a').forEach(link => {
- link.addEventListener('click', () => {
- document.querySelector('.mobile-nav').classList.remove('active');
- });
- });
- </script>
-
- <?php if ($has_newsletter): ?>
- <script src="/newsletter/newsletter-form.js?v=<?= date('YmdH') ?>"></script>
- <?php endif; ?>
- <?= flatly_custom_html('body_end') ?>
- <?= flatly_custom_js() ?>
- </body>
-
- </html>