FlatlyPage
Version 1.0.0 • 54 files • 724.77 KB
Files
.htaccess
.last_check
admin/account.php
admin/dashboard.php
admin/easyedit.js
admin/extensions.php
admin/generate-hash.php
admin/index.php
admin/logout.php
admin/preview.php
admin/scripts.php
admin/theme-edit/builder.php
admin/theme-edit/generator.php
admin/theme-edit/index.php
admin/themes.php
assets/fonts/inter/inter.css
assets/fonts/space-grotesk/space-grotesk.css
config.php
contact-handler.php
contact.php
css/admin.css
css/contact.css
css/styles.css
css/theme.css
data/.htaccess
data/index.php
data/settings.php
data/sitemap-config.php
engine/index.php
engine/renderion.php
extensions-loader.php
extensions/privimetrics/main.php
extensions/privimetrics/manifest.xml
extensions/scroll_to_top/main.php
extensions/scroll_to_top/manifest.xml
extensions/seo_image_master/main.php
extensions/seo_image_master/manifest.xml
favicons.txt
index.php
newsletter/.htaccess
newsletter/confirm.php
newsletter/manager.php
newsletter/newsletter-form.js
newsletter/newsletter-styles.css
newsletter/newsletter-unavailable.php
newsletter/newsletter.sql
newsletter/settings.php
newsletter/subscribe.php
newsletter/unsubscribe.php
page.php
robots.txt.php
sitemap.php
updater/index.php
version.txt
admin/account.php
<?php
require_once __DIR__ . '/../config.php';
require_login();
$message = '';
$message_type = '';
// Load admin data
$adminFile = DATA_DIR . '/admin.json';
$adminData = json_decode(file_get_contents($adminFile), true);
// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['csrf_token'] ?? '';
if (!verify_csrf_token($token)) {
$message = 'Invalid request. Please try again.';
$message_type = 'error';
} else {
$action = $_POST['action'] ?? '';
switch ($action) {
case 'update_username':
$newUsername = trim($_POST['new_username'] ?? '');
$currentPassword = $_POST['current_password'] ?? '';
if (empty($newUsername)) {
$message = 'Username cannot be empty.';
$message_type = 'error';
} elseif (!password_verify($currentPassword, $adminData['password'])) {
$message = 'Current password is incorrect.';
$message_type = 'error';
} else {
$adminData['username'] = $newUsername;
$adminData['updated_at'] = date('Y-m-d H:i:s');
if (file_put_contents($adminFile, json_encode($adminData, JSON_PRETTY_PRINT))) {
$_SESSION['admin_username'] = $newUsername;
$message = 'Username updated successfully!';
$message_type = 'success';
} else {
$message = 'Failed to update username.';
$message_type = 'error';
}
}
break;
case 'change_password':
$currentPassword = $_POST['current_password'] ?? '';
$newPassword = $_POST['new_password'] ?? '';
$confirmPassword = $_POST['confirm_password'] ?? '';
if (empty($currentPassword) || empty($newPassword)) {
$message = 'Please fill in all password fields.';
$message_type = 'error';
} elseif (!password_verify($currentPassword, $adminData['password'])) {
$message = 'Current password is incorrect.';
$message_type = 'error';
} elseif (strlen($newPassword) < 8) {
$message = 'New password must be at least 8 characters.';
$message_type = 'error';
} elseif ($newPassword !== $confirmPassword) {
$message = 'New passwords do not match.';
$message_type = 'error';
} else {
$adminData['password'] = password_hash($newPassword, PASSWORD_DEFAULT);
$adminData['updated_at'] = date('Y-m-d H:i:s');
if (file_put_contents($adminFile, json_encode($adminData, JSON_PRETTY_PRINT))) {
$message = 'Password changed successfully!';
$message_type = 'success';
} else {
$message = 'Failed to change password.';
$message_type = 'error';
}
}
break;
case 'update_email':
$newEmail = strtolower(trim($_POST['new_email'] ?? ''));
$currentPassword = $_POST['current_password'] ?? '';
if ($newEmail !== '' && !filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
$message = 'Please enter a valid email address.';
$message_type = 'error';
} elseif (!password_verify($currentPassword, $adminData['password'])) {
$message = 'Current password is incorrect.';
$message_type = 'error';
} else {
$adminData['email'] = $newEmail;
$adminData['updated_at'] = date('Y-m-d H:i:s');
if (file_put_contents($adminFile, json_encode($adminData, JSON_PRETTY_PRINT))) {
$message = 'Email updated successfully!';
$message_type = 'success';
} else {
$message = 'Failed to update email.';
$message_type = 'error';
}
}
break;
case 'logout':
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
header('Location: /admin');
exit;
break;
}
}
}
$csrf_token = generate_csrf_token();
$site_settings = get_site_settings();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Account Settings - <?= e($site_settings['site_name'] ?? SITE_NAME) ?></title>
<link rel="icon" href="admin.ico?v=<?= filemtime(__DIR__ . '/../css/admin.css') ?>" type="image/x-icon">
<link rel="preload" href="/assets/fonts/inter/inter.ttf" as="font" type="font/ttf" crossorigin>
<link rel="stylesheet" href="/assets/fonts/inter/inter.css">
<link rel="stylesheet" href="/css/admin.css?v=<?= filemtime(__DIR__ . '/../css/admin.css') ?>">
</head>
<body>
<div class="app">
<!-- Sidebar -->
<aside class="sidebar">
<button class="mobile-nav-toggle" onclick="document.querySelector('.sidebar').classList.remove('open')">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<line x1="6" y1="6" x2="18" y2="18"/>
<line x1="6" y1="18" x2="18" y2="6"/>
</svg>
</button>
<div class="sidebar-header">
<div class="sidebar-logo">
<img src="/logos/flatlypage_light.svg" alt="Logo" style="width: 20px;">
<span>FlatlyPage CMS</span>
</div>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-label">Content</div>
<a href="/admin/dashboard.php?tab=index" class="nav-item">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Homepage
</a>
<a href="/admin/dashboard.php?tab=products" class="nav-item">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/></svg>
Pages
</a>
</div>
<div class="nav-section">
<div class="nav-label">Site</div>
<a href="/admin/dashboard.php?tab=settings" class="nav-item">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
Settings
</a>
<a href="themes.php" class="nav-item active">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M20 7h-3a2 2 0 0 1-2-2V2"/><path d="M9 18a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h7l4 4v10a2 2 0 0 1-2 2Z"/><path d="M3 7.6v12.8A1.6 1.6 0 0 0 4.6 22h9.8"/></svg>
Themes
</a>
<a href="extensions.php" class="nav-item">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
Extensions
</a>
<a href="../updater/" class="nav-item">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <path d="M23 4v6h-6"/> <path d="M1 20v-6h6"/> <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/> </svg>
Updater
</a>
</div>
<div class="nav-section">
<div class="nav-label">Quick Actions</div>
<a href="/admin/dashboard.php?tab=new" class="nav-item">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
New Page
</a>
<a href="/" target="_blank" class="nav-item">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
View Site
</a>
</div>
</nav>
<div class="sidebar-footer">
<a href="/admin/account.php" class="user-menu active">
<div class="user-avatar">
<?= strtoupper(substr($adminData['username'], 0, 1)) ?>
</div>
<div class="user-info">
<div class="user-name">
<?= e($adminData['username']) ?>
</div>
<div class="user-role">Account Settings</div>
</div>
</a>
</div>
</aside>
<!-- Main Content -->
<main class="main">
<header class="main-header">
<button class="mobile-nav-toggle" onclick="document.querySelector('.sidebar').classList.add('open')">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
</button>
<div class="main-header-inner">
<h1>Account Settings</h1>
<div class="header-actions">
<a href="/admin/dashboard.php" class="btn btn-secondary btn-sm">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" width="16" height="16"><path d="M19 12H5"/><polyline points="12 19 5 12 12 5"/></svg>
Back to Dashboard
</a>
</div>
</div>
</header>
<div class="main-content">
<?php if ($message): ?>
<div class="message <?= $message_type ?>">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" width="20" height="20">
<?php if ($message_type === 'success'): ?>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
<?php else: ?>
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
<?php endif; ?>
</svg>
<?= e($message) ?>
</div>
<?php endif; ?>
<div style="max-width: 800px;">
<!-- Account Info Card -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<h3 class="card-title">Account Information</h3>
</div>
<div class="card-body">
<div style="display: flex; align-items: center; gap: 20px; padding: 20px; background: var(--bg); border-radius: 12px; border: 1px solid var(--border);">
<div class="user-avatar" style="width: 64px; height: 64px; font-size: 24px;">
<?= strtoupper(substr($adminData['username'], 0, 1)) ?>
</div>
<div>
<div style="font-weight: 600; font-size: 1.125rem; margin-bottom: 4px;">
<?= e($adminData['username']) ?>
</div>
<div style="color: var(--text-muted); font-size: 0.875rem;">
Administrator
</div>
<?php if (isset($adminData['created_at'])): ?>
<div style="color: var(--text-muted); font-size: 0.8125rem; margin-top: 8px;">
Account created: <?= date('F j, Y', strtotime($adminData['created_at'])) ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Change Username Card -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<h3 class="card-title">Change Username</h3>
</div>
<div class="card-body">
<form method="POST" action="">
<input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
<input type="hidden" name="action" value="update_username">
<div class="form-group">
<label class="form-label">New Username</label>
<input type="text"
name="new_username"
class="form-input"
value="<?= e($adminData['username']) ?>"
required
autocomplete="username">
</div>
<div class="form-group">
<label class="form-label">Current Password (to confirm)</label>
<input type="password"
name="current_password"
class="form-input"
required
autocomplete="current-password"
placeholder="Enter your current password">
</div>
<div style="display: flex; justify-content: flex-end; margin-top: 20px;">
<button type="submit" class="btn btn-primary">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" width="16" height="16"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
Update Username
</button>
</div>
</form>
</div>
</div>
<!-- Change Email Card -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<h3 class="card-title">Change Email</h3>
</div>
<div class="card-body">
<form method="POST" action="">
<input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
<input type="hidden" name="action" value="update_email">
<div class="form-group">
<label class="form-label">Email Address</label>
<input type="email"
name="new_email"
class="form-input"
value="<?= e($adminData['email'] ?? '') ?>"
autocomplete="email"
placeholder="you@example.com">
<p class="form-hint">Used for login security alerts</p>
</div>
<div class="form-group">
<label class="form-label">Current Password (to confirm)</label>
<input type="password"
name="current_password"
class="form-input"
required
autocomplete="current-password"
placeholder="Enter your current password">
</div>
<div style="display: flex; justify-content: flex-end; margin-top: 20px;">
<button type="submit" class="btn btn-primary">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" width="16" height="16"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
Update Email
</button>
</div>
</form>
</div>
</div>
<!-- Change Password Card -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<h3 class="card-title">Change Password</h3>
</div>
<div class="card-body">
<form method="POST" action="">
<input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
<input type="hidden" name="action" value="change_password">
<div class="form-group">
<label class="form-label">Current Password</label>
<input type="password"
name="current_password"
class="form-input"
required
autocomplete="current-password"
placeholder="Enter your current password">
</div>
<div class="form-group">
<label class="form-label">New Password</label>
<input type="password"
name="new_password"
class="form-input"
required
minlength="8"
autocomplete="new-password"
placeholder="Minimum 8 characters">
<p class="form-hint">Must be at least 8 characters long</p>
</div>
<div class="form-group">
<label class="form-label">Confirm New Password</label>
<input type="password"
name="confirm_password"
class="form-input"
required
minlength="8"
autocomplete="new-password"
placeholder="Re-enter new password">
</div>
<div style="display: flex; justify-content: flex-end; margin-top: 20px;">
<button type="submit" class="btn btn-primary">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" width="16" height="16"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
Change Password
</button>
</div>
</form>
</div>
</div>
<!-- Logout Card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Session Management</h3>
</div>
<div class="card-body">
<div style="display: flex; align-items: center; justify-content: space-between; padding: 20px; background: var(--bg); border-radius: 12px; border: 1px solid var(--border);">
<div>
<div style="font-weight: 600; margin-bottom: 4px;">Sign Out</div>
<div style="color: var(--text-muted); font-size: 0.875rem;">
End your current session and return to login
</div>
</div>
<form method="POST" action="" style="margin: 0;">
<input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
<input type="hidden" name="action" value="logout">
<button type="submit" class="btn btn-secondary">
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" width="16" height="16"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
Sign Out
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
// Auto-hide messages after 5 seconds
document.addEventListener('DOMContentLoaded', function() {
const messages = document.querySelectorAll('.message');
messages.forEach(message => {
setTimeout(() => {
message.style.opacity = '0';
setTimeout(() => {
message.remove();
}, 300);
}, 5000);
});
});
</script>
</body>
</html>