Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f26dd27edb | ||
|
|
f2c0bb39c0 |
97
admin/blog.php
Normal file
97
admin/blog.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
function create_slug($string){
|
||||
$slug = preg_replace('/[^A-Za-z0-9-]+'/, '-', strtolower($string));
|
||||
return $slug;
|
||||
}
|
||||
|
||||
// Handle form submission for adding a new blog post
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_post'])) {
|
||||
$title = $_POST['title'];
|
||||
$content = $_POST['content'];
|
||||
$slug = create_slug($title);
|
||||
|
||||
// Check if slug already exists and make it unique
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT id FROM blog_posts WHERE slug = ?");
|
||||
$stmt->execute([$slug]);
|
||||
$i = 1;
|
||||
$original_slug = $slug;
|
||||
while($stmt->fetch()){
|
||||
$slug = $original_slug . '-' . $i++;
|
||||
$stmt->execute([$slug]);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO blog_posts (title, content, slug) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$title, $content, $slug]);
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch all blog posts
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT * FROM blog_posts ORDER BY created_at DESC");
|
||||
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Manage Blog</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<h1>Manage Blog</h1>
|
||||
|
||||
<h2>Add New Post</h2>
|
||||
<form action="blog.php" method="post" class="form-container">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="content">Content</label>
|
||||
<textarea id="content" name="content" rows="10" required></textarea>
|
||||
</div>
|
||||
<button type="submit" name="add_post">Add Post</button>
|
||||
</form>
|
||||
|
||||
<h2>Existing Posts</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Created At</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($posts as $post): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($post['title']); ?></td>
|
||||
<td><?php echo htmlspecialchars($post['created_at']); ?></td>
|
||||
<td class="actions">
|
||||
<a href="edit_blog.php?id=<?php echo $post['id']; ?>">Edit</a>
|
||||
<a href="delete_blog.php?id=<?php echo $post['id']; ?>" onclick="return confirm('Are you sure you want to delete this post?');">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
172
admin/dashboard.php
Normal file
172
admin/dashboard.php
Normal file
@ -0,0 +1,172 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
|
||||
$pdo = db();
|
||||
$message = '';
|
||||
|
||||
function update_content($pdo, $key, $value) {
|
||||
$sql = "INSERT INTO site_content (section_key, section_value) VALUES (:key, :value)
|
||||
ON DUPLICATE KEY UPDATE section_value = :value";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute(['key' => $key, 'value' => $value]);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
if (isset($_POST['hero_title'])) {
|
||||
update_content($pdo, 'hero_title', $_POST['hero_title']);
|
||||
update_content($pdo, 'hero_subtitle', $_POST['hero_subtitle']);
|
||||
$message = '<div class="toast success">Hero section updated successfully!</div>';
|
||||
}
|
||||
|
||||
if (isset($_POST['about_me_content'])) {
|
||||
update_content($pdo, 'about_me', $_POST['about_me_content']);
|
||||
$message = '<div class="toast success">\"About Me\" section updated successfully!</div>';
|
||||
}
|
||||
|
||||
if (isset($_POST['add_portfolio_item'])) {
|
||||
$sql = "INSERT INTO portfolio_items (title, description, image_url, project_url) VALUES (:title, :description, :image_url, :project_url)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([
|
||||
'title' => $_POST['title'],
|
||||
'description' => $_POST['description'],
|
||||
'image_url' => $_POST['image_url'],
|
||||
'project_url' => $_POST['project_url']
|
||||
]);
|
||||
$message = '<div class="toast success">Portfolio item added successfully!</div>';
|
||||
}
|
||||
|
||||
if (isset($_POST['delete_portfolio_item'])) {
|
||||
$sql = "DELETE FROM portfolio_items WHERE id = :id";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute(['id' => $_POST['item_id']]);
|
||||
$message = '<div class="toast success">Portfolio item deleted successfully!</div>';
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$message = '<div class="toast error">Error updating content: ' . $e->getMessage() . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch site content
|
||||
$content = [];
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT section_key, section_value FROM site_content");
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$content[$row['section_key']] = $row['section_value'];
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$message = '<div class="toast error">Error fetching site content: ' . $e->getMessage() . '</div>';
|
||||
}
|
||||
|
||||
// Fetch portfolio items
|
||||
$portfolio_items = [];
|
||||
try {
|
||||
$portfolio_items = $pdo->query("SELECT * FROM portfolio_items ORDER BY sort_order ASC, created_at DESC")->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
$message = '<div class="toast error">Error fetching portfolio items: ' . $e->getMessage() . '</div>';
|
||||
}
|
||||
|
||||
|
||||
$hero_title = $content['hero_title'] ?? '';
|
||||
$hero_subtitle = $content['hero_subtitle'] ?? '';
|
||||
$about_me_content = $content['about_me'] ?? '';
|
||||
|
||||
?>
|
||||
|
||||
<div class="dashboard-header">
|
||||
<h1>Dashboard</h1>
|
||||
<a href="logout.php" class="btn-logout">Logout</a>
|
||||
</div>
|
||||
|
||||
<?php if ($message) echo $message; ?>
|
||||
|
||||
<div class="dashboard-container">
|
||||
<div class="content-editor">
|
||||
<h2>Manage Portfolio</h2>
|
||||
|
||||
<form action="dashboard.php" method="POST" class="portfolio-form">
|
||||
<input type="hidden" name="add_portfolio_item" value="1">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="image_url">Image URL</label>
|
||||
<input type="text" id="image_url" name="image_url" placeholder="https://example.com/image.jpg">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="project_url">Project URL</label>
|
||||
<input type="text" id="project_url" name="project_url" placeholder="https://example.com/project">
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Add Portfolio Item</button>
|
||||
</form>
|
||||
|
||||
<div class="portfolio-list">
|
||||
<h3>Existing Items</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Image</th>
|
||||
<th>Title</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($portfolio_items)): ?>
|
||||
<tr><td colspan="3">No portfolio items yet.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($portfolio_items as $item):
|
||||
// Ensure image_url is not empty, provide placeholder if it is
|
||||
$imageUrl = $item['image_url'] ?: 'https://via.placeholder.com/100x60';
|
||||
?>
|
||||
<tr>
|
||||
<td><img src="<?php echo htmlspecialchars($imageUrl); ?>" alt="Thumbnail" class="thumbnail"></td>
|
||||
<td><?php echo htmlspecialchars($item['title']); ?></td>
|
||||
<td>
|
||||
<form action="dashboard.php" method="POST" onsubmit="return confirm('Are you sure you want to delete this item?');">
|
||||
<input type="hidden" name="delete_portfolio_item" value="1">
|
||||
<input type="hidden" name="item_id" value="<?php echo $item['id']; ?>">
|
||||
<button type="submit" class="btn-delete">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="info-box">
|
||||
<p>Welcome, <strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>!</p>
|
||||
<p>You can manage your website content from here.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const toast = document.querySelector('.toast');
|
||||
if (toast) {
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '0';
|
||||
setTimeout(() => toast.remove(), 500);
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
21
admin/delete_blog.php
Normal file
21
admin/delete_blog.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (!$id) {
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("DELETE FROM blog_posts WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
21
admin/delete_portfolio.php
Normal file
21
admin/delete_portfolio.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (!$id) {
|
||||
header('Location: portfolio.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("DELETE FROM portfolio_items WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
|
||||
header('Location: portfolio.php');
|
||||
exit();
|
||||
84
admin/edit_blog.php
Normal file
84
admin/edit_blog.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
function create_slug($string){
|
||||
$slug = preg_replace('/[^A-Za-z0-9-]+'/, '-', strtolower($string));
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (!$id) {
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Handle form submission for updating the blog post
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_post'])) {
|
||||
$title = $_POST['title'];
|
||||
$content = $_POST['content'];
|
||||
$slug = create_slug($title);
|
||||
|
||||
// Check if slug already exists and make it unique
|
||||
$stmt = $pdo->prepare("SELECT id FROM blog_posts WHERE slug = ? AND id != ?");
|
||||
$stmt->execute([$slug, $id]);
|
||||
$i = 1;
|
||||
$original_slug = $slug;
|
||||
while($stmt->fetch()){
|
||||
$slug = $original_slug . '-' . $i++;
|
||||
$stmt->execute([$slug, $id]);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE blog_posts SET title = ?, content = ?, slug = ? WHERE id = ?");
|
||||
$stmt->execute([$title, $content, $slug, $id]);
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch the blog post
|
||||
$stmt = $pdo->prepare("SELECT * FROM blog_posts WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$post = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$post) {
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Blog Post</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<h1>Edit Blog Post</h1>
|
||||
|
||||
<form action="edit_blog.php?id=<?php echo $post['id']; ?>" method="post" class="form-container">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" value="<?php echo htmlspecialchars($post['title']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="content">Content</label>
|
||||
<textarea id="content" name="content" rows="10" required><?php echo htmlspecialchars($post['content']); ?></textarea>
|
||||
</div>
|
||||
<button type="submit" name="update_post">Update Post</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
95
admin/edit_portfolio.php
Normal file
95
admin/edit_portfolio.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (!$id) {
|
||||
header('Location: portfolio.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Handle form submission for updating the portfolio item
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_item'])) {
|
||||
$title = $_POST['title'];
|
||||
$description = $_POST['description'];
|
||||
$image_url = $_POST['existing_image'];
|
||||
|
||||
// Handle image upload
|
||||
if (isset($_FILES['image']) && $_FILES['image']['error'] == 0) {
|
||||
$target_dir = "../assets/images/portfolio/";
|
||||
$target_file = $target_dir . basename($_FILES["image"]["name"]);
|
||||
if (move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)) {
|
||||
$image_url = "assets/images/portfolio/" . basename($_FILES["image"]["name"]);
|
||||
} else {
|
||||
$error = "Sorry, there was an error uploading your file.";
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($error)) {
|
||||
$stmt = $pdo->prepare("UPDATE portfolio_items SET title = ?, description = ?, image_url = ? WHERE id = ?");
|
||||
$stmt->execute([$title, $description, $image_url, $id]);
|
||||
header('Location: portfolio.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the portfolio item
|
||||
$stmt = $pdo->prepare("SELECT * FROM portfolio_items WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$item = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$item) {
|
||||
header('Location: portfolio.php');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Portfolio Item</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<h1>Edit Portfolio Item</h1>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<p class="error"><?php echo htmlspecialchars($error); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="edit_portfolio.php?id=<?php echo $item['id']; ?>" method="post" enctype="multipart/form-data" class="form-container">
|
||||
<input type="hidden" name="existing_image" value="<?php echo htmlspecialchars($item['image_url']); ?>">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" value="<?php echo htmlspecialchars($item['title']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="4" required><?php echo htmlspecialchars($item['description']); ?></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="image">Image</label>
|
||||
<input type="file" id="image" name="image" accept="image/*">
|
||||
<?php if ($item['image_url']): ?>
|
||||
<p>Current image:</p>
|
||||
<img src="../<?php echo htmlspecialchars($item['image_url']); ?>" alt="<?php echo htmlspecialchars($item['title']); ?>" width="100">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<button type="submit" name="update_item">Update Item</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
2
admin/footer.php
Normal file
2
admin/footer.php
Normal file
@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
23
admin/header.php
Normal file
23
admin/header.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
run_migrations();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Panel</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="admin-nav">
|
||||
<a href="dashboard.php" class="nav-item"><i class="fas fa-fw fa-tachometer-alt"></i> Dashboard</a>
|
||||
<a href="submissions.php" class="nav-item"><i class="fas fa-fw fa-envelope"></i> Submissions</a>
|
||||
<a href="portfolio.php" class="nav-item"><i class="fas fa-fw fa-briefcase"></i> Portfolio</a>
|
||||
<a href="blog.php" class="nav-item"><i class="fas fa-fw fa-blog"></i> Blog</a>
|
||||
<a href="settings.php" class="nav-item"><i class="fas fa-fw fa-cog"></i> Settings</a>
|
||||
<a href="logout.php" class="nav-item nav-logout"><i class="fas fa-fw fa-sign-out-alt"></i> Logout</a>
|
||||
</nav>
|
||||
|
||||
33
admin/index.php
Normal file
33
admin/index.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// If user is already logged in, redirect to dashboard
|
||||
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="login-container">
|
||||
<h2>Admin Login</h2>
|
||||
<?php
|
||||
if (isset($_GET['error'])) {
|
||||
echo '<p class="error">' . htmlspecialchars($_GET['error']) . '</p>';
|
||||
}
|
||||
?>
|
||||
<form action="login.php" method="post">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
29
admin/login.php
Normal file
29
admin/login.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Hardcoded credentials
|
||||
$valid_username = 'admin';
|
||||
$hashed_password = '$2y$10$xQL0pDXzugm9cK871EfhzuxasM6rnXMLaMZ74WxQZ2cnBXA11Wv5.'; // Hashed for "password123"
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($username === $valid_username && password_verify($password, $hashed_password)) {
|
||||
// Password is correct, so start a new session
|
||||
session_regenerate_id();
|
||||
$_SESSION['loggedin'] = true;
|
||||
$_SESSION['username'] = $username;
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
} else {
|
||||
// Invalid credentials
|
||||
header('Location: index.php?error=Invalid username or password');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
// Not a POST request
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
21
admin/logout.php
Normal file
21
admin/logout.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Unset all of the session variables
|
||||
$_SESSION = [];
|
||||
|
||||
// Destroy the session.
|
||||
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();
|
||||
|
||||
// Redirect to login page
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
?>
|
||||
108
admin/portfolio.php
Normal file
108
admin/portfolio.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Handle form submission for adding a new portfolio item
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_item'])) {
|
||||
$title = $_POST['title'];
|
||||
$description = $_POST['description'];
|
||||
$image_url = '';
|
||||
|
||||
// Handle image upload
|
||||
if (isset($_FILES['image']) && $_FILES['image']['error'] == 0) {
|
||||
$target_dir = "../assets/images/portfolio/";
|
||||
if (!is_dir($target_dir)) {
|
||||
mkdir($target_dir, 0777, true);
|
||||
}
|
||||
$target_file = $target_dir . basename($_FILES["image"]["name"]);
|
||||
if (move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)) {
|
||||
$image_url = "assets/images/portfolio/" . basename($_FILES["image"]["name"]);
|
||||
} else {
|
||||
$error = "Sorry, there was an error uploading your file.";
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($error)) {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("INSERT INTO portfolio_items (title, description, image_url) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$title, $description, $image_url]);
|
||||
header('Location: portfolio.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all portfolio items
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT * FROM portfolio_items ORDER BY id DESC");
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Manage Portfolio</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<h1>Manage Portfolio</h1>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<p class="error"><?php echo htmlspecialchars($error); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>Add New Item</h2>
|
||||
<form action="portfolio.php" method="post" enctype="multipart/form-data" class="form-container">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="4" required></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="image">Image</label>
|
||||
<input type="file" id="image" name="image" accept="image/*" required>
|
||||
</div>
|
||||
<button type="submit" name="add_item">Add Item</button>
|
||||
</form>
|
||||
|
||||
<h2>Existing Items</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Image</th>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td><img src="../<?php echo htmlspecialchars($item['image_url']); ?>" alt="<?php echo htmlspecialchars($item['title']); ?>" width="100"></td>
|
||||
<td><?php echo htmlspecialchars($item['title']); ?></td>
|
||||
<td><?php echo htmlspecialchars($item['description']); ?></td>
|
||||
<td class="actions">
|
||||
<a href="edit_portfolio.php?id=<?php echo $item['id']; ?>">Edit</a>
|
||||
<a href="delete_portfolio.php?id=<?php echo $item['id']; ?>" onclick="return confirm('Are you sure you want to delete this item?');">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
97
admin/settings.php
Normal file
97
admin/settings.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
|
||||
$pdo = db();
|
||||
$message = '';
|
||||
|
||||
function update_content($pdo, $key, $value) {
|
||||
$sql = "INSERT INTO site_content (section_key, section_value) VALUES (:key, :value)
|
||||
ON DUPLICATE KEY UPDATE section_value = :value";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute(['key' => $key, 'value' => $value]);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
if (isset($_POST['hero_title'])) {
|
||||
update_content($pdo, 'hero_title', $_POST['hero_title']);
|
||||
update_content($pdo, 'hero_subtitle', $_POST['hero_subtitle']);
|
||||
$message = '<div class="toast success">Hero section updated successfully!</div>';
|
||||
}
|
||||
|
||||
if (isset($_POST['about_me_content'])) {
|
||||
update_content($pdo, 'about_me', $_POST['about_me_content']);
|
||||
$message = '<div class="toast success">"About Me" section updated successfully!</div>';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$message = '<div class="toast error">Error updating content: ' . $e->getMessage() . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch site content
|
||||
$content = [];
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT section_key, section_value FROM site_content");
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$content[$row['section_key']] = $row['section_value'];
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$message = '<div class="toast error">Error fetching site content: ' . $e->getMessage() . '</div>';
|
||||
}
|
||||
|
||||
$hero_title = $content['hero_title'] ?? '';
|
||||
$hero_subtitle = $content['hero_subtitle'] ?? '';
|
||||
$about_me_content = $content['about_me'] ?? '';
|
||||
|
||||
?>
|
||||
|
||||
<div class="dashboard-header">
|
||||
<h1>Site Settings</h1>
|
||||
<a href="logout.php" class="btn-logout">Logout</a>
|
||||
</div>
|
||||
|
||||
<?php if ($message) echo $message; ?>
|
||||
|
||||
<div class="dashboard-container">
|
||||
<div class="content-editor">
|
||||
<h2>Edit Hero Section</h2>
|
||||
<form action="settings.php" method="POST">
|
||||
<label for="hero_title">Title</label>
|
||||
<input type="text" id="hero_title" name="hero_title" value="<?php echo htmlspecialchars($hero_title); ?>">
|
||||
|
||||
<label for="hero_subtitle">Subtitle</label>
|
||||
<input type="text" id="hero_subtitle" name="hero_subtitle" value="<?php echo htmlspecialchars($hero_subtitle); ?>">
|
||||
|
||||
<button type="submit" class="btn-submit">Save Hero Content</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="content-editor">
|
||||
<h2>Edit "About Me" Section</h2>
|
||||
<form action="settings.php" method="POST">
|
||||
<textarea name="about_me_content" rows="10"><?php echo htmlspecialchars($about_me_content); ?></textarea>
|
||||
<button type="submit" class="btn-submit">Save About Me</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const toast = document.querySelector('.toast');
|
||||
if (toast) {
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '0';
|
||||
setTimeout(() => toast.remove(), 500);
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
256
admin/style.css
Normal file
256
admin/style.css
Normal file
@ -0,0 +1,256 @@
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
color: #343A40;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 300px;
|
||||
margin: 100px auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #4B88A2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #3A6A7E;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #D8000C;
|
||||
background-color: #FFD2D2;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
background-color: #343A40;
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dashboard-header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.dashboard-header a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.dashboard-container {
|
||||
padding: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.content-editor {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
||||
margin-bottom: 20px; /* Add margin between editors */
|
||||
}
|
||||
|
||||
.content-editor:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
.content-editor h2 {
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content-editor textarea, .content-editor input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
width: auto;
|
||||
padding: 10px 20px;
|
||||
background-color: #4B88A2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
background-color: #3A6A7E;
|
||||
}
|
||||
|
||||
.btn-logout {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
padding: 8px 15px;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-logout:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background-color: #e9ecef;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
/* Toast Notifications */
|
||||
.toast {
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin: 20px;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.toast.success {
|
||||
background-color: #28a745;
|
||||
}
|
||||
|
||||
.toast.error {
|
||||
background-color: #dc3545;
|
||||
}
|
||||
|
||||
/* Portfolio Styles */
|
||||
.portfolio-form {
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.portfolio-list table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.portfolio-list th, .portfolio-list td {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.portfolio-list th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
width: 100px;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn-delete:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
/* Adjust grid for more content */
|
||||
.dashboard-container {
|
||||
grid-template-columns: 3fr 1fr; /* Give more space to the main content */
|
||||
}
|
||||
|
||||
.content-editor {
|
||||
grid-column: 1 / 2;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / 3; /* Span across the top rows */
|
||||
}
|
||||
|
||||
/* Admin Navigation */
|
||||
.admin-nav {
|
||||
display: flex;
|
||||
background-color: #343A40;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #4a4a4a;
|
||||
}
|
||||
.nav-item {
|
||||
color: #fff;
|
||||
padding: 15px 20px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.nav-item:hover {
|
||||
background-color: #4B88A2;
|
||||
}
|
||||
.nav-logout {
|
||||
margin-left: auto;
|
||||
}
|
||||
65
admin/submissions.php
Normal file
65
admin/submissions.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT id, name, email, message, created_at FROM contact_submissions ORDER BY created_at DESC");
|
||||
$submissions = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
// For development, you might want to log this error.
|
||||
// For production, show a generic error message.
|
||||
die("Could not connect to the database. Please try again later.");
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h3 mb-4 text-gray-800">Contact Form Submissions</h1>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Received Messages</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Message</th>
|
||||
<th>Received At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($submissions)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">No submissions yet.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($submissions as $submission): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($submission['id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($submission['name']); ?></td>
|
||||
<td><a href="mailto:<?php echo htmlspecialchars($submission['email']); ?>"><?php echo htmlspecialchars($submission['email']); ?></a></td>
|
||||
<td><?php echo nl2br(htmlspecialchars($submission['message'])); ?></td>
|
||||
<td><?php echo htmlspecialchars($submission['created_at']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
391
assets/css/custom.css
Normal file
391
assets/css/custom.css
Normal file
@ -0,0 +1,391 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--primary-color: #00c6ff;
|
||||
--secondary-color: #0072ff;
|
||||
--background-image: url('https://images.pexels.com/photos/1287145/pexels-photo-1287145.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2');
|
||||
--text-color: #f0f0f0;
|
||||
--heading-color: #ffffff;
|
||||
--glass-bg: rgba(255, 255, 255, 0.1);
|
||||
--glass-border: rgba(255, 255, 255, 0.2);
|
||||
--blur-strength: 10px;
|
||||
--border-radius: 1rem;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text-color);
|
||||
background-image: var(--background-image);
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-weight: 700;
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(var(--blur-strength));
|
||||
-webkit-backdrop-filter: blur(var(--blur-strength));
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.navbar .navbar-brand {
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-nav .nav-link {
|
||||
color: var(--text-color);
|
||||
font-weight: 400;
|
||||
transition: color 0.2s;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-nav .nav-link:hover,
|
||||
.navbar-dark .navbar-nav .nav-link.active {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.hero {
|
||||
color: white;
|
||||
padding: 12rem 0;
|
||||
background: transparent;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 4.5rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.hero .lead {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
text-shadow: 0 1px 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
padding: 0.8rem 2rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
box-shadow: 0 4px 20px rgba(0, 150, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 25px rgba(0, 150, 255, 0.5);
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 6rem 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
margin-bottom: 4rem;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(var(--blur-strength));
|
||||
-webkit-backdrop-filter: blur(var(--blur-strength));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 2rem;
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
#about .img-fluid {
|
||||
border: 3px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.portfolio-item .card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(var(--blur-strength));
|
||||
-webkit-backdrop-filter: blur(var(--blur-strength));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.portfolio-item .card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.portfolio-item .card .card-title {
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
.portfolio-item .card .card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.card-img-top {
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#contact {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 0.5rem;
|
||||
color: var(--text-color);
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.form-control::placeholder {
|
||||
color: rgba(240, 240, 240, 0.6);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 0 0 0.25rem rgba(0, 198, 255, 0.25);
|
||||
border-color: var(--primary-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(var(--blur-strength));
|
||||
-webkit-backdrop-filter: blur(var(--blur-strength));
|
||||
border-top: 1px solid var(--glass-border);
|
||||
color: var(--text-color);
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--text-color);
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 1055;
|
||||
}
|
||||
|
||||
/* Add a background to sections for readability */
|
||||
#about, #portfolio, #contact {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#portfolio {
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.blog-card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(var(--blur-strength));
|
||||
-webkit-backdrop-filter: blur(var(--blur-strength));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--border-radius);
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.blog-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.blog-card .card-title {
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
color: #fff;
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.blog-post {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(var(--blur-strength));
|
||||
-webkit-backdrop-filter: blur(var(--blur-strength));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 3rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.blog-post h1 {
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
.blog-post .text-muted {
|
||||
color: rgba(240, 240, 240, 0.7) !important;
|
||||
}
|
||||
|
||||
.blog-post section {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.blog-listing {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.blog-post-preview {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.blog-post-preview:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 12px 40px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.blog-post-preview h3 {
|
||||
color: var(--heading-color);
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.blog-post-preview .date {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(240, 240, 240, 0.7);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.blog-post-preview p {
|
||||
flex-grow: 1;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.blog-post-preview .btn-outline-primary {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.post-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.post-header h1 {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.post-header .post-meta {
|
||||
font-size: 1rem;
|
||||
color: rgba(240, 240, 240, 0.7);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.9;
|
||||
}
|
||||
|
||||
.share-section {
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.share-section h4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.share-buttons a {
|
||||
color: var(--text-color);
|
||||
margin: 0 0.5rem;
|
||||
font-size: 1.5rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.share-buttons a:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.search-container {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.search-results-container {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.search-result-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 30px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.search-result-item h4 {
|
||||
color: var(--heading-color);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.search-result-item p {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.search-result-item .date {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(240, 240, 240, 0.7);
|
||||
}
|
||||
|
||||
134
assets/js/main.js
Normal file
134
assets/js/main.js
Normal file
@ -0,0 +1,134 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
// Smooth scrolling for anchor links
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const targetEl = document.querySelector(this.getAttribute('href'));
|
||||
if (targetEl) {
|
||||
targetEl.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Activate scrollspy
|
||||
const mainNav = document.body;
|
||||
if (mainNav) {
|
||||
const scrollSpy = new bootstrap.ScrollSpy(document.body, {
|
||||
target: '#navbarNav',
|
||||
offset: 100,
|
||||
});
|
||||
}
|
||||
|
||||
// Close mobile nav on item click
|
||||
const navLinks = document.querySelectorAll('.navbar-nav .nav-link');
|
||||
const navbarCollapse = document.querySelector('.navbar-collapse');
|
||||
navLinks.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
if (navbarCollapse.classList.contains('show')) {
|
||||
const bsCollapse = new bootstrap.Collapse(navbarCollapse, {
|
||||
toggle: false
|
||||
});
|
||||
bsCollapse.hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Contact Form Submission
|
||||
const contactForm = document.getElementById('contactForm');
|
||||
if (contactForm) {
|
||||
contactForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = e.target;
|
||||
const formData = new FormData(form);
|
||||
const submitButton = form.querySelector('button[type="submit"]');
|
||||
const originalButtonText = submitButton.innerHTML;
|
||||
|
||||
submitButton.disabled = true;
|
||||
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Sending...';
|
||||
|
||||
fetch('contact.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('Success!', 'Your message has been sent successfully.', 'success');
|
||||
form.reset();
|
||||
} else {
|
||||
showToast('Error!', data.error || 'An unknown error occurred.', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('Error!', 'A network error occurred. Please try again.', 'danger');
|
||||
})
|
||||
.finally(() => {
|
||||
submitButton.disabled = false;
|
||||
submitButton.innerHTML = originalButtonText;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Toast function
|
||||
function showToast(title, message, type) {
|
||||
const toastContainer = document.getElementById('toast-container');
|
||||
if (!toastContainer) return;
|
||||
|
||||
const toastEl = document.createElement('div');
|
||||
toastEl.className = `toast align-items-center text-white bg-${type} border-0`;
|
||||
toastEl.role = 'alert';
|
||||
toastEl.ariaLive = 'assertive';
|
||||
toastEl.ariaAtomic = 'true';
|
||||
|
||||
toastEl.innerHTML = `
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
<strong>${title}</strong> ${message}
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
toastContainer.appendChild(toastEl);
|
||||
|
||||
const toast = new bootstrap.Toast(toastEl, { delay: 5000 });
|
||||
toast.show();
|
||||
|
||||
toastEl.addEventListener('hidden.bs.toast', () => {
|
||||
toastEl.remove();
|
||||
});
|
||||
}
|
||||
|
||||
// Social Sharing
|
||||
const shareButtons = document.querySelector('.share-buttons');
|
||||
if (shareButtons) {
|
||||
const postUrl = encodeURIComponent(window.location.href);
|
||||
const postTitle = encodeURIComponent(document.title);
|
||||
|
||||
const twitterButton = shareButtons.querySelector('a[title="Share on Twitter"]');
|
||||
if (twitterButton) {
|
||||
twitterButton.href = `https://twitter.com/intent/tweet?url=${postUrl}&text=${postTitle}`;
|
||||
twitterButton.target = '_blank';
|
||||
twitterButton.rel = 'noopener noreferrer';
|
||||
}
|
||||
|
||||
const facebookButton = shareButtons.querySelector('a[title="Share on Facebook"]');
|
||||
if (facebookButton) {
|
||||
facebookButton.href = `https://www.facebook.com/sharer/sharer.php?u=${postUrl}`;
|
||||
facebookButton.target = '_blank';
|
||||
facebookButton.rel = 'noopener noreferrer';
|
||||
}
|
||||
|
||||
const linkedinButton = shareButtons.querySelector('a[title="Share on LinkedIn"]');
|
||||
if (linkedinButton) {
|
||||
linkedinButton.href = `https://www.linkedin.com/shareArticle?mini=true&url=${postUrl}&title=${postTitle}`;
|
||||
linkedinButton.target = '_blank';
|
||||
linkedinButton.rel = 'noopener noreferrer';
|
||||
}
|
||||
}
|
||||
});
|
||||
150
blog.php
Normal file
150
blog.php
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$search_term = $_GET['search'] ?? '';
|
||||
|
||||
// Fetch blog posts
|
||||
$pdo = db();
|
||||
$posts = [];
|
||||
try {
|
||||
if ($search_term) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM blog_posts WHERE title LIKE ? OR content LIKE ? ORDER BY created_at DESC");
|
||||
$stmt->execute(['%' . $search_term . '%', '%' . $search_term . '%']);
|
||||
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$posts = $pdo->query("SELECT * FROM blog_posts ORDER BY created_at DESC")->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log('Could not fetch blog posts: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$project_name = "Blog";
|
||||
$project_description = "Read our latest articles.";
|
||||
$project_keywords = "blog, articles, finance, web development";
|
||||
$project_image_url = "https://project-screens.s3.amazonaws.com/screenshots/30971/app-hero-20251016-105317.png";
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title><?php echo htmlspecialchars($project_name); ?></title>
|
||||
<meta name="description" content="<?php echo htmlspecialchars($project_description); ?>">
|
||||
<meta name="keywords" content="<?php echo htmlspecialchars($project_keywords); ?>">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="<?php echo htmlspecialchars($project_name); ?>">
|
||||
<meta property="og:description" content="<?php echo htmlspecialchars($project_description); ?>">
|
||||
<meta property="og:image" content="<?php echo htmlspecialchars($project_image_url); ?>">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="<?php echo htmlspecialchars($project_name); ?>">
|
||||
<meta property="twitter:description" content="<?php echo htmlspecialchars($project_description); ?>">
|
||||
<meta property="twitter:image" content="<?php echo htmlspecialchars($project_image_url); ?>">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.php">Alex</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#home">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#portfolio">Portfolio</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="blog.php">Blog</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#contact">Contact</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/admin" target="_blank">Admin</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<header class="hero text-center">
|
||||
<div class="container">
|
||||
<h1 class="display-4">Blog</h1>
|
||||
<p class="lead">Read our latest articles.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="search-container glass-card p-4 mb-5">
|
||||
<form action="blog.php" method="GET">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control form-control-lg" name="search" placeholder="Search for articles..." value="<?php echo htmlspecialchars($search_term); ?>">
|
||||
<button class="btn btn-primary" type="submit" id="button-addon2">
|
||||
<i data-feather="search" class="align-middle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if ($search_term && !empty($posts)): ?>
|
||||
<h2 class="section-title text-center mb-4">Search Results for "<?php echo htmlspecialchars($search_term); ?>"</h2>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($posts)): ?>
|
||||
<div class="glass-card text-center p-5">
|
||||
<h2 class="mb-3"><?php echo $search_term ? 'No Results Found' : 'Coming Soon'; ?></h2>
|
||||
<p class="lead"><?php echo $search_term ? 'Sorry, no articles matched your search. Try another term.' : 'No blog posts have been added yet. Check back soon for insights and articles.'; ?></p>
|
||||
<?php if ($search_term): ?>
|
||||
<a href="blog.php" class="btn btn-outline-primary mt-3">Back to Blog</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="<?php echo $search_term ? 'search-results-container' : 'blog-listing'; ?>">
|
||||
<?php foreach ($posts as $post): ?>
|
||||
<?php if ($search_term): ?>
|
||||
<div class="search-result-item">
|
||||
<a href="post.php?slug=<?php echo htmlspecialchars($post['slug']); ?>" class="text-decoration-none">
|
||||
<h4><?php echo htmlspecialchars($post['title']); ?></h4>
|
||||
</a>
|
||||
<p><?php echo htmlspecialchars(substr(strip_tags($post['content']), 0, 200)); ?>...</p>
|
||||
<div class="date">Published on <?php echo date("F j, Y", strtotime($post['created_at'])); ?></div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="blog-post-preview">
|
||||
<h3><?php echo htmlspecialchars($post['title']); ?></h3>
|
||||
<div class="date">Published on <?php echo date("F j, Y", strtotime($post['created_at'])); ?></div>
|
||||
<p><?php echo htmlspecialchars(substr(strip_tags($post['content']), 0, 150)); ?>...</p>
|
||||
<a href="post.php?slug=<?php echo htmlspecialchars($post['slug']); ?>" class="btn btn-outline-primary">Read More <i data-feather="arrow-right" class="align-middle" style="width:16px;"></i></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="text-center">
|
||||
<div class="container">
|
||||
<div class="mb-3">
|
||||
<a href="#" class="text-white mx-2"><i data-feather="twitter"></i></a>
|
||||
<a href="#" class="text-white mx-2"><i data-feather="linkedin"></i></a>
|
||||
<a href="#" class="text-white mx-2"><i data-feather="github"></i></a>
|
||||
</div>
|
||||
<p>© <?php echo date("Y"); ?> Alex. All Rights Reserved. | <a href="privacy.php">Privacy Policy</a></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
59
contact.php
Normal file
59
contact.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
require_once __DIR__ . '/mail/MailService.php';
|
||||
|
||||
// Run migrations to ensure table exists
|
||||
try {
|
||||
run_migrations();
|
||||
} catch (Exception $e) {
|
||||
// Log error but don't block execution if table already exists
|
||||
error_log('Migration failed: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$response = ['success' => false, 'error' => 'Invalid request'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$message = trim($_POST['message'] ?? '');
|
||||
|
||||
if (empty($name) || empty($email) || empty($message)) {
|
||||
$response['error'] = 'Please fill in all fields.';
|
||||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$response['error'] = 'Invalid email format.';
|
||||
} else {
|
||||
try {
|
||||
// 1. Save to database
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("INSERT INTO contact_submissions (name, email, message) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$name, $email, $message]);
|
||||
|
||||
// 2. Send email notification
|
||||
$mail_to = getenv('MAIL_TO') ?: 'owner@example.com'; // Fallback email
|
||||
$subject = 'New Contact Form Submission from ' . $name;
|
||||
$email_body = "You have a new message from your website contact form.\n\n" .
|
||||
"Name: {$name}\n" .
|
||||
"Email: {$email}\n" .
|
||||
"Message:\n{$message}";
|
||||
|
||||
MailService::sendMail($mail_to, $subject, nl2br(htmlspecialchars($email_body)), $email_body, ['reply_to' => $email]);
|
||||
|
||||
$response['success'] = true;
|
||||
unset($response['error']);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log('Database Error: ' . $e->getMessage());
|
||||
$response['error'] = 'Could not save your message. Please try again later.';
|
||||
} catch (Exception $e) {
|
||||
error_log('Mail Error: ' . $e->getMessage());
|
||||
// The message was saved to DB, so we can consider this a partial success.
|
||||
// For the user, it's a full success, but we log the mail error.
|
||||
$response['success'] = true;
|
||||
unset($response['error']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
@ -15,3 +15,13 @@ function db() {
|
||||
}
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
function run_migrations() {
|
||||
$pdo = db();
|
||||
$migrations_dir = __DIR__ . '/migrations/';
|
||||
$files = glob($migrations_dir . '*.sql');
|
||||
foreach ($files as $file) {
|
||||
$sql = file_get_contents($file);
|
||||
$pdo->exec($sql);
|
||||
}
|
||||
}
|
||||
|
||||
7
db/migrations/001_create_contact_submissions.sql
Normal file
7
db/migrations/001_create_contact_submissions.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS contact_submissions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
9
db/migrations/002_create_site_content_table.sql
Normal file
9
db/migrations/002_create_site_content_table.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS `site_content` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`section_key` VARCHAR(100) NOT NULL UNIQUE,
|
||||
`section_value` TEXT,
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Seed the about section with some default text
|
||||
INSERT INTO `site_content` (section_key, section_value) VALUES ('about_me', 'This is the default about me text. You can edit this in the admin panel.') ON DUPLICATE KEY UPDATE section_key=section_key;
|
||||
9
db/migrations/003_create_portfolio_items_table.sql
Normal file
9
db/migrations/003_create_portfolio_items_table.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS portfolio_items (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
image_url VARCHAR(2048),
|
||||
project_url VARCHAR(2048),
|
||||
sort_order INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
@ -0,0 +1,3 @@
|
||||
ALTER TABLE portfolio_items
|
||||
ADD COLUMN sort_order INT DEFAULT 0,
|
||||
ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
10
db/migrations/005_create_blog_posts_table.sql
Normal file
10
db/migrations/005_create_blog_posts_table.sql
Normal file
@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS `blog_posts` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`slug` varchar(255) NOT NULL,
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `slug` (`slug`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
330
index.php
330
index.php
@ -1,150 +1,194 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
// Run migrations on initial load
|
||||
try {
|
||||
run_migrations();
|
||||
} catch (Exception $e) {
|
||||
error_log('Could not run migrations: ' . $e->getMessage());
|
||||
// Optional: add a user-facing error message if the DB is critical for the page to load.
|
||||
}
|
||||
|
||||
// Fetch all site content
|
||||
$pdo = db();
|
||||
$content = [];
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT section_key, section_value FROM site_content");
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$content[$row['section_key']] = $row['section_value'];
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log('Could not fetch site content: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$about_me_content = $content['about_me'] ?? 'Welcome to your portfolio! You can edit this text in the admin panel.';
|
||||
$hero_title = $content['hero_title'] ?? 'Creative Developer & Designer';
|
||||
$hero_subtitle = $content['hero_subtitle'] ?? 'I build beautiful and functional websites.';
|
||||
|
||||
// Fetch portfolio items
|
||||
$portfolio_items = [];
|
||||
try {
|
||||
$portfolio_items = $pdo->query("SELECT * FROM portfolio_items ORDER BY sort_order ASC, created_at DESC")->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
error_log('Could not fetch portfolio items: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
|
||||
$project_name = "Personal Portfolio";
|
||||
$project_description = "Showcase Your Work and Connect: A Personal Portfolio to Impress and Engage with Potential Clients.";
|
||||
$project_keywords = "personal portfolio, creative portfolio, web developer portfolio, designer portfolio, online resume, contact form, project showcase, freelance portfolio";
|
||||
$project_image_url = "https://project-screens.s3.amazonaws.com/screenshots/30971/app-hero-20251016-105317.png";
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
?>
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes bg-pan {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.hint {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title><?php echo htmlspecialchars($project_name); ?></title>
|
||||
<meta name="description" content="<?php echo htmlspecialchars($project_description); ?>">
|
||||
<meta name="keywords" content="<?php echo htmlspecialchars($project_keywords); ?>">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="<?php echo htmlspecialchars($project_name); ?>">
|
||||
<meta property="og:description" content="<?php echo htmlspecialchars($project_description); ?>">
|
||||
<meta property="og:image" content="<?php echo htmlspecialchars($project_image_url); ?>">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="<?php echo htmlspecialchars($project_name); ?>">
|
||||
<meta property="twitter:description" content="<?php echo htmlspecialchars($project_description); ?>">
|
||||
<meta property="twitter:image" content="<?php echo htmlspecialchars($project_image_url); ?>">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
<body data-bs-spy="scroll" data-bs-target="#navbarNav" data-bs-offset="100">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="#home">Alex</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link active" href="#home">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#portfolio">Portfolio</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="blog.php">Blog</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/admin" target="_blank">Admin</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<header id="home" class="hero text-center">
|
||||
<div class="container">
|
||||
<h1 class="display-4"><?php echo htmlspecialchars($hero_title); ?></h1>
|
||||
<p class="lead"><?php echo htmlspecialchars($hero_subtitle); ?></p>
|
||||
<a href="#portfolio" class="btn btn-primary btn-lg mt-3">View My Work</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section id="about">
|
||||
<div class="container">
|
||||
<div class="glass-card">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-4 text-center">
|
||||
<img src="https://picsum.photos/id/237/300/300" class="img-fluid rounded-circle mb-4 mb-lg-0" alt="Alex">
|
||||
</div>
|
||||
<div class="col-lg-8">
|
||||
<h2 class="section-title mb-4">About Me</h2>
|
||||
<p class="lead"><?php echo nl2br(htmlspecialchars($about_me_content)); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="portfolio">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Portfolio</h2>
|
||||
<div class="row">
|
||||
<?php if (empty($portfolio_items)): ?>
|
||||
<div class="col-12 text-center">
|
||||
<div class="glass-card">
|
||||
<p>No portfolio items have been added yet. Check back soon!</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($portfolio_items as $item): ?>
|
||||
<div class="col-md-4 portfolio-item">
|
||||
<div class="card h-100">
|
||||
<img src="<?php echo htmlspecialchars($item['image_url'] ?: 'https://picsum.photos/seed/'.htmlspecialchars($item['id']).'/600/400'); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($item['title']); ?>">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title"><?php echo htmlspecialchars($item['title']); ?></h5>
|
||||
<p class="card-text"><?php echo nl2br(htmlspecialchars($item['description'])); ?></p>
|
||||
<?php if (!empty($item['project_url'])): ?>
|
||||
<a href="<?php echo htmlspecialchars($item['project_url']); ?>" class="btn btn-sm btn-outline-primary mt-auto" target="_blank" rel="noopener noreferrer">Learn More</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contact">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="glass-card">
|
||||
<h2 class="section-title">Get In Touch</h2>
|
||||
<form id="contactForm" novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" placeholder="Your Name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" placeholder="Your Email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">Message</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="5" placeholder="Your Message" required></textarea>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-primary">Send Message</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="text-center">
|
||||
<div class="container">
|
||||
<div class="mb-3">
|
||||
<a href="#" class="text-white mx-2"><i data-feather="twitter"></i></a>
|
||||
<a href="#" class="text-white mx-2"><i data-feather="linkedin"></i></a>
|
||||
<a href="#" class="text-white mx-2"><i data-feather="github"></i></a>
|
||||
</div>
|
||||
<p>© <?php echo date("Y"); ?> Alex. All Rights Reserved. | <a href="privacy.php">Privacy Policy</a></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div id="toast-container" class="toast-container"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
119
post.php
Normal file
119
post.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$slug = $_GET['slug'] ?? null;
|
||||
if (!$slug) {
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch the blog post
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM blog_posts WHERE slug = ?");
|
||||
$stmt->execute([$slug]);
|
||||
$post = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$post) {
|
||||
// Optional: redirect to a 404 page
|
||||
header('Location: blog.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$project_name = htmlspecialchars($post['title']);
|
||||
$project_description = htmlspecialchars(substr($post['content'], 0, 160));
|
||||
$project_keywords = "blog, articles, finance, web development";
|
||||
$project_image_url = "https://project-screens.s3.amazonaws.com/screenshots/30971/app-hero-20251016-105317.png";
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title><?php echo $project_name; ?></title>
|
||||
<meta name="description" content="<?php echo $project_description; ?>">
|
||||
<meta name="keywords" content="<?php echo htmlspecialchars($project_keywords); ?>">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="<?php echo $project_name; ?>">
|
||||
<meta property="og:description" content="<?php echo $project_description; ?>">
|
||||
<meta property="og:image" content="<?php echo htmlspecialchars($project_image_url); ?>">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:title" content="<?php echo $project_name; ?>">
|
||||
<meta property="twitter:description" content="<?php echo $project_description; ?>">
|
||||
<meta property="twitter:image" content="<?php echo htmlspecialchars($project_image_url); ?>">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.php">Alex</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#home">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#portfolio">Portfolio</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="blog.php">Blog</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#contact">Contact</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/admin" target="_blank">Admin</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container mt-5 pt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-9">
|
||||
<article class="blog-post">
|
||||
<header class="post-header">
|
||||
<h1 class="mb-3"><?php echo htmlspecialchars($post['title']); ?></h1>
|
||||
<div class="post-meta">Posted on <?php echo date('F j, Y', strtotime($post['created_at'])); ?></div>
|
||||
</header>
|
||||
<section class="post-content mb-5">
|
||||
<?php echo nl2br($post['content']); ?>
|
||||
</section>
|
||||
<div class="share-section">
|
||||
<h4>Share This Post</h4>
|
||||
<div class="share-buttons">
|
||||
<a href="#" title="Share on Twitter"><i data-feather="twitter"></i></a>
|
||||
<a href="#" title="Share on Facebook"><i data-feather="facebook"></i></a>
|
||||
<a href="#" title="Share on LinkedIn"><i data-feather="linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<div class="text-center mt-5">
|
||||
<a href="blog.php" class="btn btn-outline-primary btn-lg"><i data-feather="arrow-left" class="align-middle"></i> Back to Blog</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="text-center">
|
||||
<div class="container">
|
||||
<div class="mb-3">
|
||||
<a href="#" class="text-white mx-2"><i data-feather="twitter"></i></a>
|
||||
<a href="#" class="text-white mx-2"><i data-feather="linkedin"></i></a>
|
||||
<a href="#" class="text-white mx-2"><i data-feather="github"></i></a>
|
||||
</div>
|
||||
<p>© <?php echo date("Y"); ?> Alex. All Rights Reserved. | <a href="privacy.php">Privacy Policy</a></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
59
privacy.php
Normal file
59
privacy.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
$page_title = "Privacy Policy";
|
||||
$page_description = "Privacy Policy for our website.";
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($page_title); ?> - Personal Portfolio</title>
|
||||
<meta name="description" content="<?php echo htmlspecialchars($page_description); ?>">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.php">Alex</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#home">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#portfolio">Portfolio</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#contact">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container" style="padding-top: 8rem; padding-bottom: 6rem;">
|
||||
<h1>Privacy Policy</h1>
|
||||
<p>This is a placeholder for your privacy policy. You should replace this text with your own policy.</p>
|
||||
<p>Your privacy is important to us. It is our policy to respect your privacy regarding any information we may collect from you across our website.</p>
|
||||
<h2>1. Information we collect</h2>
|
||||
<p>We only ask for personal information when we truly need it to provide a service to you. We collect it by fair and lawful means, with your knowledge and consent. We also let you know why we’re collecting it and how it will be used.</p>
|
||||
<h2>2. How we use your information</h2>
|
||||
<p>We only retain collected information for as long as necessary to provide you with your requested service. What data we store, we’ll protect within commercially acceptable means to prevent loss and theft, as well as unauthorized access, disclosure, copying, use or modification.</p>
|
||||
<h2>3. Cookies</h2>
|
||||
<p>Our website may use cookies to enhance user experience.</p>
|
||||
<h2>4. Links to other sites</h2>
|
||||
<p>Our website may link to external sites that are not operated by us. Please be aware that we have no control over the content and practices of these sites, and cannot accept responsibility or liability for their respective privacy policies.</p>
|
||||
<h2>5. Your consent</h2>
|
||||
<p>By using our website, you hereby consent to our privacy policy and agree to its terms.</p>
|
||||
<a href="index.php" class="btn btn-primary mt-4">Back to Home</a>
|
||||
</main>
|
||||
|
||||
<footer class="text-center">
|
||||
<div class="container">
|
||||
<p>© <?php echo date("Y"); ?> Your Name. All Rights Reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user