Auto commit: 2025-12-17T23:56:00.480Z
This commit is contained in:
parent
dfb123b21a
commit
0ba5997fad
155
admin/categories.php
Normal file
155
admin/categories.php
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'admin') {
|
||||||
|
header("Location: ../login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Handle form submissions for categories
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['add_category'])) {
|
||||||
|
$name = trim($_POST['category_name']);
|
||||||
|
if (!empty($name)) {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO categories (name) VALUES (?)");
|
||||||
|
$stmt->execute([$name]);
|
||||||
|
}
|
||||||
|
} elseif (isset($_POST['update_category'])) {
|
||||||
|
$id = $_POST['category_id'];
|
||||||
|
$name = trim($_POST['category_name']);
|
||||||
|
$visibility = isset($_POST['visibility']) ? 1 : 0;
|
||||||
|
$order = (int)$_POST['display_order'];
|
||||||
|
$stmt = $pdo->prepare("UPDATE categories SET name = ?, visibility = ?, display_order = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$name, $visibility, $order, $id]);
|
||||||
|
} elseif (isset($_POST['delete_category'])) {
|
||||||
|
$id = $_POST['category_id'];
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM categories WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
} elseif (isset($_POST['add_subcategory'])) {
|
||||||
|
$name = trim($_POST['subcategory_name']);
|
||||||
|
$category_id = $_POST['category_id'];
|
||||||
|
if (!empty($name)) {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO subcategories (category_id, name) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$category_id, $name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header("Location: categories.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all categories and subcategories
|
||||||
|
$categories = $pdo->query("SELECT * FROM categories ORDER BY display_order ASC, name ASC")->fetchAll();
|
||||||
|
$subcategories = [];
|
||||||
|
$stmt = $pdo->query("SELECT * FROM subcategories ORDER BY name ASC");
|
||||||
|
while ($row = $stmt->fetch()) {
|
||||||
|
$subcategories[$row['category_id']][] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manage Categories - Admin Panel</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><a href="/" style="text-decoration: none; color: inherit;">Admin Panel</a></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="../index.php">View Site</a>
|
||||||
|
<a href="../logout.php">Logout</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container my-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<aside class="admin-nav">
|
||||||
|
<h3>Menu</h3>
|
||||||
|
<nav class="nav flex-column">
|
||||||
|
<a class="nav-link" href="index.php">Dashboard</a>
|
||||||
|
<a class="nav-link active" href="categories.php">Categories</a>
|
||||||
|
<a class="nav-link" href="users.php">Users</a>
|
||||||
|
<a class="nav-link" href="links.php">Links</a>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<main class="content">
|
||||||
|
<h2>Manage Categories</h2>
|
||||||
|
|
||||||
|
<!-- Add Category Form -->
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header">Add New Category</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" action="categories.php">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" name="category_name" placeholder="New category name" required>
|
||||||
|
<button class="btn btn-primary" type="submit" name="add_category">Add Category</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Category List -->
|
||||||
|
<?php foreach ($categories as $category): ?>
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" action="categories.php" class="d-flex align-items-center">
|
||||||
|
<input type="hidden" name="category_id" value="<?php echo $category['id']; ?>">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<input type="text" class="form-control" name="category_name" value="<?php echo htmlspecialchars($category['name']); ?>">
|
||||||
|
</div>
|
||||||
|
<div class="ms-3">
|
||||||
|
<label class="form-check-label me-2">Visible:</label>
|
||||||
|
<input class="form-check-input" type="checkbox" name="visibility" <?php echo $category['visibility'] ? 'checked' : ''; ?>>
|
||||||
|
</div>
|
||||||
|
<div class="ms-3" style="width: 80px;">
|
||||||
|
<input type="number" class="form-control" name="display_order" value="<?php echo $category['display_order']; ?>">
|
||||||
|
</div>
|
||||||
|
<div class="ms-3">
|
||||||
|
<button type="submit" name="update_category" class="btn btn-sm btn-success">Save</button>
|
||||||
|
<button type="submit" name="delete_category" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure?')">Del</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Subcategories -->
|
||||||
|
<div class="mt-3 ms-4">
|
||||||
|
<h6>Subcategories</h6>
|
||||||
|
<ul>
|
||||||
|
<?php if (isset($subcategories[$category['id']])): ?>
|
||||||
|
<?php foreach ($subcategories[$category['id']] as $sub): ?>
|
||||||
|
<li><?php echo htmlspecialchars($sub['name']); ?></li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<li>No subcategories yet.</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
<form method="POST" action="categories.php" class="input-group input-group-sm">
|
||||||
|
<input type="hidden" name="category_id" value="<?php echo $category['id']; ?>">
|
||||||
|
<input type="text" class="form-control" name="subcategory_name" placeholder="New subcategory" required>
|
||||||
|
<button class="btn btn-outline-secondary" type="submit" name="add_subcategory">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
83
admin/index.php
Normal file
83
admin/index.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'admin') {
|
||||||
|
header("Location: ../login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
|
// Fetch some basic stats for the dashboard
|
||||||
|
$pdo = db();
|
||||||
|
$user_count = $pdo->query("SELECT count(*) FROM users")->fetchColumn();
|
||||||
|
$link_count = $pdo->query("SELECT count(*) FROM links")->fetchColumn();
|
||||||
|
$category_count = $pdo->query("SELECT count(*) FROM categories")->fetchColumn();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Admin Panel - <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><a href="/" style="text-decoration: none; color: inherit;">Admin Panel</a></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="../index.php">View Site</a>
|
||||||
|
<a href="../logout.php">Logout</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container my-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<aside class="admin-nav">
|
||||||
|
<h3>Menu</h3>
|
||||||
|
<nav class="nav flex-column">
|
||||||
|
<a class="nav-link active" href="index.php">Dashboard</a>
|
||||||
|
<a class="nav-link" href="categories.php">Categories</a>
|
||||||
|
<a class="nav-link" href="users.php">Users</a>
|
||||||
|
<a class="nav-link" href="links.php">Links</a>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<main class="content">
|
||||||
|
<h2>Dashboard</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center p-3">
|
||||||
|
<h3><?php echo $user_count; ?></h3>
|
||||||
|
<p>Total Users</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center p-3">
|
||||||
|
<h3><?php echo $link_count; ?></h3>
|
||||||
|
<p>Total Links</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card text-center p-3">
|
||||||
|
<h3><?php echo $category_count; ?></h3>
|
||||||
|
<p>Total Categories</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
99
admin/links.php
Normal file
99
admin/links.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'admin') {
|
||||||
|
header("Location: ../login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$links = $pdo->query("SELECT l.*, u.username, s.name as subcategory_name, c.name as category_name
|
||||||
|
FROM links l
|
||||||
|
JOIN users u ON l.user_id = u.id
|
||||||
|
JOIN subcategories s ON l.subcategory_id = s.id
|
||||||
|
JOIN categories c ON s.category_id = c.id
|
||||||
|
ORDER BY l.created_at DESC")->fetchAll();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manage Links - Admin Panel</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><a href="/" style="text-decoration: none; color: inherit;">Admin Panel</a></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="../index.php">View Site</a>
|
||||||
|
<a href="../logout.php">Logout</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container-fluid my-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<aside class="admin-nav">
|
||||||
|
<h3>Menu</h3>
|
||||||
|
<nav class="nav flex-column">
|
||||||
|
<a class="nav-link" href="index.php">Dashboard</a>
|
||||||
|
<a class="nav-link" href="categories.php">Categories</a>
|
||||||
|
<a class="nav-link" href="users.php">Users</a>
|
||||||
|
<a class="nav-link active" href="links.php">Links</a>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<main class="content">
|
||||||
|
<h2>Manage Links</h2>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>URL</th>
|
||||||
|
<th>Category</th>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($links)): ?>
|
||||||
|
<tr><td colspan="7">No links submitted yet.</td></tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($links as $link): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($link['title']); ?></td>
|
||||||
|
<td><a href="<?php echo htmlspecialchars($link['url']); ?>" target="_blank"><?php echo htmlspecialchars(substr($link['url'], 0, 50)); ?>...</a></td>
|
||||||
|
<td><?php echo htmlspecialchars($link['category_name']); ?> > <?php echo htmlspecialchars($link['subcategory_name']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($link['username']); ?></td>
|
||||||
|
<td><span class="badge bg-<?php echo $link['status'] === 'approved' ? 'success' : ($link['status'] === 'pending' ? 'warning' : 'danger'); ?>"><?php echo htmlspecialchars($link['status']); ?></span></td>
|
||||||
|
<td><?php echo date("Y-m-d", strtotime($link['created_at'])); ?></td>
|
||||||
|
<td>
|
||||||
|
<!-- Future actions -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
105
admin/users.php
Normal file
105
admin/users.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'admin') {
|
||||||
|
header("Location: ../login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Handle user role updates
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_role'])) {
|
||||||
|
$user_id = $_POST['user_id'];
|
||||||
|
$role = $_POST['role'];
|
||||||
|
// Add extra validation for role value
|
||||||
|
if (in_array($role, ['regular', 'power_user', 'admin'])) {
|
||||||
|
$stmt = $pdo->prepare("UPDATE users SET role = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$role, $user_id]);
|
||||||
|
}
|
||||||
|
header("Location: users.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = $pdo->query("SELECT id, username, role, created_at FROM users ORDER BY created_at DESC")->fetchAll();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manage Users - Admin Panel</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><a href="/" style="text-decoration: none; color: inherit;">Admin Panel</a></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="../index.php">View Site</a>
|
||||||
|
<a href="../logout.php">Logout</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container my-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<aside class="admin-nav">
|
||||||
|
<h3>Menu</h3>
|
||||||
|
<nav class="nav flex-column">
|
||||||
|
<a class="nav-link" href="index.php">Dashboard</a>
|
||||||
|
<a class="nav-link" href="categories.php">Categories</a>
|
||||||
|
<a class="nav-link active" href="users.php">Users</a>
|
||||||
|
<a class="nav-link" href="links.php">Links</a>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<main class="content">
|
||||||
|
<h2>Manage Users</h2>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Registered</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($users as $user): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($user['username']); ?></td>
|
||||||
|
<td>
|
||||||
|
<form method="POST" action="users.php" class="d-inline">
|
||||||
|
<input type="hidden" name="user_id" value="<?php echo $user['id']; ?>">
|
||||||
|
<select name="role" class="form-select form-select-sm" style="width: auto; display: inline-block;">
|
||||||
|
<option value="regular" <?php echo ($user['role'] === 'regular') ? 'selected' : ''; ?>>Regular</option>
|
||||||
|
<option value="power_user" <?php echo ($user['role'] === 'power_user') ? 'selected' : ''; ?>>Power User</option>
|
||||||
|
<option value="admin" <?php echo ($user['role'] === 'admin') ? 'selected' : ''; ?>>Admin</option>
|
||||||
|
</select>
|
||||||
|
<button type="submit" name="update_role" class="btn btn-sm btn-primary">Update</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td><?php echo date("Y-m-d", strtotime($user['created_at'])); ?></td>
|
||||||
|
<td>
|
||||||
|
<!-- Future actions like delete or view profile -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
135
assets/css/custom.css
Normal file
135
assets/css/custom.css
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
body {
|
||||||
|
background: #f0f0f0 url('https://www.transparenttextures.com/patterns/lined-paper.png');
|
||||||
|
font-family: Verdana, Geneva, sans-serif;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: linear-gradient(to bottom, #eaeaea, #cccccc);
|
||||||
|
border-bottom: 1px solid #999;
|
||||||
|
padding: 15px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-family: Georgia, Times, 'Times New Roman', serif;
|
||||||
|
font-size: 2.5em;
|
||||||
|
color: #000080; /* Classic Blue */
|
||||||
|
margin: 0;
|
||||||
|
text-shadow: 2px 2px 3px #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-links a {
|
||||||
|
margin-left: 15px;
|
||||||
|
color: #000080;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-list {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-right: 1px solid #dee2e6;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-list h3 {
|
||||||
|
font-family: Georgia, Times, "Times New Roman", serif;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #0048ad;
|
||||||
|
border-bottom: 2px solid #f2c94c;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-list .nav-link {
|
||||||
|
color: #0000FF;
|
||||||
|
padding: 5px 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-list .nav-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2 {
|
||||||
|
font-family: Georgia, Times, "Times New Roman", serif;
|
||||||
|
color: #0048ad;
|
||||||
|
border-bottom: 2px solid #ccc;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item:hover {
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item .thumbnail {
|
||||||
|
width: 120px;
|
||||||
|
height: 90px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
margin-right: 15px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item-body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item-title a {
|
||||||
|
color: #0000FF;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item-title a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item-url {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #006400; /* Dark Green */
|
||||||
|
margin-bottom: 5px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-item-description {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
background-color: #343a40;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
1
assets/js/main.js
Normal file
1
assets/js/main.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
// Future javascript for interactivity
|
||||||
58
db/migrations/001_initial_schema.sql
Normal file
58
db/migrations/001_initial_schema.sql
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `users` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`username` VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
`password` VARCHAR(255) NOT NULL,
|
||||||
|
`role` ENUM('regular', 'power_user', 'admin') NOT NULL DEFAULT 'regular',
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `categories` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
`visibility` BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
`display_order` INT NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `subcategories` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`category_id` INT NOT NULL,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `links` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`user_id` INT NOT NULL,
|
||||||
|
`subcategory_id` INT NOT NULL,
|
||||||
|
`title` VARCHAR(255) NOT NULL,
|
||||||
|
`url` VARCHAR(2083) NOT NULL,
|
||||||
|
`description` TEXT,
|
||||||
|
`thumbnail_url` VARCHAR(2083),
|
||||||
|
`status` ENUM('pending', 'approved', 'rejected') NOT NULL DEFAULT 'pending',
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`),
|
||||||
|
FOREIGN KEY (`subcategory_id`) REFERENCES `subcategories`(`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `moderation_logs` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`link_id` INT NOT NULL,
|
||||||
|
`moderator_id` INT NOT NULL,
|
||||||
|
`action` ENUM('approved', 'rejected') NOT NULL,
|
||||||
|
`notes` TEXT,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (`link_id`) REFERENCES `links`(`id`),
|
||||||
|
FOREIGN KEY (`moderator_id`) REFERENCES `users`(`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `visits` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`link_id` INT,
|
||||||
|
`user_id` INT,
|
||||||
|
`ip_address` VARCHAR(45),
|
||||||
|
`user_agent` TEXT,
|
||||||
|
`visited_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (`link_id`) REFERENCES `links`(`id`),
|
||||||
|
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
|
||||||
|
);
|
||||||
230
export.mysql
Normal file
230
export.mysql
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*M!999999\- enable the sandbox mode */
|
||||||
|
-- MariaDB dump 10.19 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64)
|
||||||
|
--
|
||||||
|
-- Host: 127.0.0.1 Database: app_37018
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Server version 10.11.14-MariaDB-0+deb12u2
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40101 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `categories`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `categories`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `categories` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`visibility` tinyint(1) NOT NULL DEFAULT 1,
|
||||||
|
`display_order` int(11) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `categories`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `categories` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
|
||||||
|
INSERT INTO `categories` VALUES
|
||||||
|
(1,'Arts & Entertainment',1,0),
|
||||||
|
(2,'Business & Economy',1,0),
|
||||||
|
(3,'Computers & Internet',1,0),
|
||||||
|
(4,'Education',1,0),
|
||||||
|
(5,'Government',1,0),
|
||||||
|
(6,'Health & Fitness',1,0),
|
||||||
|
(7,'Home & Garden',1,0),
|
||||||
|
(8,'News & Media',1,0),
|
||||||
|
(9,'Recreation & Sports',1,0),
|
||||||
|
(10,'Reference',1,0),
|
||||||
|
(11,'Science & Technology',1,0),
|
||||||
|
(12,'Shopping',1,0),
|
||||||
|
(13,'Society & Culture',1,0),
|
||||||
|
(14,'Travel & Tourism',1,0),
|
||||||
|
(15,'Cars & Vehicles',1,0),
|
||||||
|
(16,'Food & Drink',1,0),
|
||||||
|
(17,'Law & Legal Issues',1,0),
|
||||||
|
(18,'Pets & Animals',1,0),
|
||||||
|
(19,'Real Estate',1,0),
|
||||||
|
(20,'Games',1,0);
|
||||||
|
/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `links`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `links`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `links` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` int(11) NOT NULL,
|
||||||
|
`subcategory_id` int(11) NOT NULL,
|
||||||
|
`title` varchar(255) NOT NULL,
|
||||||
|
`url` varchar(2083) NOT NULL,
|
||||||
|
`description` text DEFAULT NULL,
|
||||||
|
`thumbnail_url` varchar(2083) DEFAULT NULL,
|
||||||
|
`status` enum('pending','approved','rejected') NOT NULL DEFAULT 'pending',
|
||||||
|
`created_at` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `user_id` (`user_id`),
|
||||||
|
KEY `subcategory_id` (`subcategory_id`),
|
||||||
|
CONSTRAINT `links_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
|
||||||
|
CONSTRAINT `links_ibfk_2` FOREIGN KEY (`subcategory_id`) REFERENCES `subcategories` (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `links`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `links` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `links` DISABLE KEYS */;
|
||||||
|
INSERT INTO `links` VALUES
|
||||||
|
(1,1,1,'test title','https://title.com','this is a test description',NULL,'approved','2025-12-17 15:34:06');
|
||||||
|
/*!40000 ALTER TABLE `links` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `moderation_logs`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `moderation_logs`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `moderation_logs` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`link_id` int(11) NOT NULL,
|
||||||
|
`moderator_id` int(11) NOT NULL,
|
||||||
|
`action` enum('approved','rejected') NOT NULL,
|
||||||
|
`notes` text DEFAULT NULL,
|
||||||
|
`created_at` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `link_id` (`link_id`),
|
||||||
|
KEY `moderator_id` (`moderator_id`),
|
||||||
|
CONSTRAINT `moderation_logs_ibfk_1` FOREIGN KEY (`link_id`) REFERENCES `links` (`id`),
|
||||||
|
CONSTRAINT `moderation_logs_ibfk_2` FOREIGN KEY (`moderator_id`) REFERENCES `users` (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `moderation_logs`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `moderation_logs` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `moderation_logs` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `moderation_logs` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `subcategories`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `subcategories`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `subcategories` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`category_id` int(11) NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `category_id` (`category_id`),
|
||||||
|
CONSTRAINT `subcategories_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `subcategories`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `subcategories` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `subcategories` DISABLE KEYS */;
|
||||||
|
INSERT INTO `subcategories` VALUES
|
||||||
|
(1,1,'test Arts & Entertainment'),
|
||||||
|
(2,11,'Science & Technology syb');
|
||||||
|
/*!40000 ALTER TABLE `subcategories` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `users`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `users`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `users` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`username` varchar(50) NOT NULL,
|
||||||
|
`password` varchar(255) NOT NULL,
|
||||||
|
`role` enum('regular','power_user','admin') NOT NULL DEFAULT 'regular',
|
||||||
|
`created_at` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `username` (`username`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `users`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `users` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
|
||||||
|
INSERT INTO `users` VALUES
|
||||||
|
(1,'admin','$2y$10$ZXgcZZeRqeZmt3gD1hqnVedgdgGwQ4R5dFoY6YRT.GY0StKYwnx5.','admin','2025-12-17 15:27:26');
|
||||||
|
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `visits`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `visits`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `visits` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`link_id` int(11) DEFAULT NULL,
|
||||||
|
`user_id` int(11) DEFAULT NULL,
|
||||||
|
`ip_address` varchar(45) DEFAULT NULL,
|
||||||
|
`user_agent` text DEFAULT NULL,
|
||||||
|
`visited_at` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `link_id` (`link_id`),
|
||||||
|
KEY `user_id` (`user_id`),
|
||||||
|
CONSTRAINT `visits_ibfk_1` FOREIGN KEY (`link_id`) REFERENCES `links` (`id`),
|
||||||
|
CONSTRAINT `visits_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `visits`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `visits` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `visits` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `visits` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
|
-- Dump completed on 2025-12-17 23:45:53
|
||||||
305
index.php
305
index.php
@ -1,150 +1,169 @@
|
|||||||
<?php
|
<?php session_start(); ?>
|
||||||
declare(strict_types=1);
|
<!DOCTYPE html>
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
?>
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>New Style</title>
|
|
||||||
<?php
|
<title><?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></title>
|
||||||
// Read project preview data from environment
|
<meta name="description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'A public web directory of curated links.'); ?>">
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
<!-- Open Graph / Facebook -->
|
||||||
?>
|
<meta property="og:type" content="website">
|
||||||
<?php if ($projectDescription): ?>
|
<meta property="og:title" content="<?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>">
|
||||||
<!-- Meta description -->
|
<meta property="og:description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'A public web directory of curated links.'); ?>">
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
<!-- Twitter -->
|
||||||
<!-- Twitter meta tags -->
|
<meta property="twitter:card" content="summary_large_image">
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
<meta property="twitter:title" content="<?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>">
|
||||||
<?php endif; ?>
|
<meta property="twitter:description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'A public web directory of curated links.'); ?>">
|
||||||
<?php if ($projectImageUrl): ?>
|
<meta property="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<!-- Twitter image -->
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
<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>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
|
||||||
<div class="card">
|
<?php
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
require_once __DIR__ . '/db/config.php';
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
|
||||||
<span class="sr-only">Loading…</span>
|
$pdo = db();
|
||||||
</div>
|
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
// Fetch visible categories from the database
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
$stmt = $pdo->query("SELECT * FROM categories WHERE visibility = 1 ORDER BY display_order ASC, name ASC");
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
$categories = $stmt->fetchAll();
|
||||||
|
|
||||||
|
// Determine the current category
|
||||||
|
$current_category_id = null;
|
||||||
|
$current_category_name = 'All Categories';
|
||||||
|
if (isset($_GET['category']) && filter_var($_GET['category'], FILTER_VALIDATE_INT)) {
|
||||||
|
$category_id_from_get = (int)$_GET['category'];
|
||||||
|
$stmt = $pdo->prepare("SELECT id, name FROM categories WHERE id = ? AND visibility = 1");
|
||||||
|
$stmt->execute([$category_id_from_get]);
|
||||||
|
$cat = $stmt->fetch();
|
||||||
|
if ($cat) {
|
||||||
|
$current_category_id = $cat['id'];
|
||||||
|
$current_category_name = $cat['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch links for the current category
|
||||||
|
$link_stmt = null;
|
||||||
|
if ($current_category_id) {
|
||||||
|
$link_stmt = $pdo->prepare(
|
||||||
|
"SELECT l.*, s.name as subcategory_name FROM links l " .
|
||||||
|
"JOIN subcategories s ON l.subcategory_id = s.id " .
|
||||||
|
"WHERE s.category_id = ? AND l.status = \'approved\' ORDER BY s.name ASC, l.created_at DESC"
|
||||||
|
);
|
||||||
|
$link_stmt->execute([$current_category_id]);
|
||||||
|
} else {
|
||||||
|
$link_stmt = $pdo->query(
|
||||||
|
"SELECT l.*, s.name as subcategory_name, c.name as category_name " .
|
||||||
|
"FROM links l " .
|
||||||
|
"JOIN subcategories s ON l.subcategory_id = s.id " .
|
||||||
|
"JOIN categories c ON s.category_id = c.id " .
|
||||||
|
"WHERE l.status = 'approved' " .
|
||||||
|
"ORDER BY c.name ASC, s.name ASC, l.created_at DESC"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$current_links = $link_stmt->fetchAll();
|
||||||
|
|
||||||
|
error_log("DEBUG: GET Parameters: " . json_encode($_GET));
|
||||||
|
error_log("DEBUG: Current Category ID: " . ($current_category_id ?? "NULL"));
|
||||||
|
error_log("DEBUG: Current Category Name: " . $current_category_name);
|
||||||
|
error_log("DEBUG: SQL Query for links: " . ($current_category_id ? $link_stmt->queryString : "All Categories Query"));
|
||||||
|
error_log("DEBUG: Current Links Count: " . count($current_links));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<a href="submit.php">Submit Link</a>
|
||||||
|
<?php if ($_SESSION['user_role'] === 'admin'): ?>
|
||||||
|
<a href="admin/index.php">Admin Panel</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="logout.php">Logout</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="register.php">Register</a>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</header>
|
||||||
<footer>
|
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<div class="container my-4">
|
||||||
</footer>
|
<div class="row">
|
||||||
|
<!-- Categories Sidebar -->
|
||||||
|
<div class="col-md-3">
|
||||||
|
<aside class="category-list">
|
||||||
|
<h3>Categories</h3>
|
||||||
|
<nav class="nav flex-column">
|
||||||
|
<a class="nav-link <?php echo ($current_category_id === null) ? 'fw-bold' : ''; ?>" href="index.php">All Categories</a>
|
||||||
|
<?php foreach ($categories as $category): ?>
|
||||||
|
<a class="nav-link <?php echo ($category['id'] === $current_category_id) ? 'fw-bold' : ''; ?>" href="?category=<?php echo $category['id']; ?>">
|
||||||
|
<?php echo htmlspecialchars($category['name']); ?>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="col-md-9">
|
||||||
|
<main class="content">
|
||||||
|
<h2><?php echo htmlspecialchars($current_category_name); ?></h2>
|
||||||
|
|
||||||
|
<?php if (empty($current_links)): ?>
|
||||||
|
<p>No links found in this category yet.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php
|
||||||
|
$current_category_for_display = null;
|
||||||
|
$current_subcategory = null;
|
||||||
|
foreach ($current_links as $link):
|
||||||
|
if ($current_category_id === null && isset($link['category_name']) && $link['category_name'] !== $current_category_for_display) {
|
||||||
|
$current_category_for_display = $link['category_name'];
|
||||||
|
echo '<h3>' . htmlspecialchars($current_category_for_display) . '</h3>';
|
||||||
|
$current_subcategory = null; // Reset subcategory when category changes
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($link['subcategory_name'] !== $current_subcategory) {
|
||||||
|
$current_subcategory = $link['subcategory_name'];
|
||||||
|
echo '<h4>' . htmlspecialchars($current_subcategory) . '</h4>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="link-item">
|
||||||
|
<img src="<?php echo htmlspecialchars($link['thumbnail']); ?>" alt="Thumbnail for <?php echo htmlspecialchars($link['title']); ?>" class="thumbnail">
|
||||||
|
<div class="link-item-body">
|
||||||
|
<div class="link-item-title">
|
||||||
|
<a href="<?php echo htmlspecialchars($link['url']); ?>" target="_blank" rel="noopener noreferrer">
|
||||||
|
<?php echo htmlspecialchars($link['title']); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<a href="<?php echo htmlspecialchars($link['url']); ?>" target="_blank" rel="noopener noreferrer" class="link-item-url"><?php echo htmlspecialchars($link['url']); ?></a>
|
||||||
|
<p class="link-item-description">
|
||||||
|
<?php echo htmlspecialchars($link['description']); ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
100
login.php
Normal file
100
login.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
if (isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if (empty($username)) {
|
||||||
|
$errors[] = 'Username is required.';
|
||||||
|
}
|
||||||
|
if (empty($password)) {
|
||||||
|
$errors[] = 'Password is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT id, username, password, role FROM users WHERE username = ?");
|
||||||
|
$stmt->execute([$username]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user['password'])) {
|
||||||
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['username'] = $user['username'];
|
||||||
|
$_SESSION['user_role'] = $user['role'];
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$errors[] = 'Invalid username or password.';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$errors[] = "Database error: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login - <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><a href="/" style="text-decoration: none; color: inherit;"><?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></a></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="register.php">Register</a>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container my-4">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<main class="content p-4">
|
||||||
|
<h2>Login</h2>
|
||||||
|
|
||||||
|
<?php if (!empty($errors)): ?>
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<?php foreach ($errors as $error): ?>
|
||||||
|
<p class="mb-0"><?php echo $error; ?></p>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form action="login.php" method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Login</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
logout.php
Normal file
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
107
register.php
Normal file
107
register.php
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$password_confirm = $_POST['password_confirm'] ?? '';
|
||||||
|
|
||||||
|
if (empty($username)) {
|
||||||
|
$errors[] = 'Username is required.';
|
||||||
|
}
|
||||||
|
if (empty($password)) {
|
||||||
|
$errors[] = 'Password is required.';
|
||||||
|
}
|
||||||
|
if ($password !== $password_confirm) {
|
||||||
|
$errors[] = 'Passwords do not match.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
|
||||||
|
$stmt->execute([$username]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$errors[] = 'Username already taken.';
|
||||||
|
} else {
|
||||||
|
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)");
|
||||||
|
// For now, all new users are 'regular'. The first admin will be created manually.
|
||||||
|
$stmt->execute([$username, $hashed_password, 'regular']);
|
||||||
|
$success = true;
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$errors[] = "Database error: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Register - <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><a href="/" style="text-decoration: none; color: inherit;"><?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></a></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<a href="register.php">Register</a>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container my-4">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<main class="content p-4">
|
||||||
|
<h2>Register</h2>
|
||||||
|
|
||||||
|
<?php if (!empty($errors)): ?>
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<?php foreach ($errors as $error): ?>
|
||||||
|
<p class="mb-0"><?php echo $error; ?></p>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<p class="mb-0">Registration successful! You can now <a href="login.php">login</a>.</p>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form action="register.php" method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password_confirm" class="form-label">Confirm Password</label>
|
||||||
|
<input type="password" class="form-control" id="password_confirm" name="password_confirm" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Register</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
160
submit.php
Normal file
160
submit.php
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$errors = [];
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
// Fetch categories and subcategories for the form
|
||||||
|
$categories = $pdo->query("SELECT * FROM categories WHERE visibility = 1 ORDER BY display_order ASC, name ASC")->fetchAll();
|
||||||
|
$subcategories = [];
|
||||||
|
if (!empty($categories)) {
|
||||||
|
$stmt = $pdo->query("SELECT * FROM subcategories ORDER BY name ASC");
|
||||||
|
while ($row = $stmt->fetch()) {
|
||||||
|
$subcategories[$row['category_id']][] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
$title = trim($_POST['title'] ?? '');
|
||||||
|
$url = trim($_POST['url'] ?? '');
|
||||||
|
$description = trim($_POST['description'] ?? '');
|
||||||
|
$subcategory_id = $_POST['subcategory_id'] ?? null;
|
||||||
|
|
||||||
|
if (empty($title)) $errors[] = 'Title is required.';
|
||||||
|
if (empty($url)) $errors[] = 'URL is required.';
|
||||||
|
if (!filter_var($url, FILTER_VALIDATE_URL)) $errors[] = 'Invalid URL.';
|
||||||
|
if (empty($subcategory_id)) $errors[] = 'Subcategory is required.';
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
// Determine status based on user role
|
||||||
|
$status = ($_SESSION['user_role'] === 'admin' || $_SESSION['user_role'] === 'power_user') ? 'approved' : 'pending';
|
||||||
|
|
||||||
|
// For now, thumbnail is not implemented
|
||||||
|
$thumbnail_url = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO links (user_id, subcategory_id, title, url, description, thumbnail_url, status) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$_SESSION['user_id'], $subcategory_id, $title, $url, $description, $thumbnail_url, $status]);
|
||||||
|
$success = true;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$errors[] = "Database error: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Submit a Link - <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<h1><a href="/" style="text-decoration: none; color: inherit;"><?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></a></h1>
|
||||||
|
<div class="auth-links">
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<a href="submit.php">Submit Link</a>
|
||||||
|
<a href="logout.php">Logout</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="register.php">Register</a>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container my-4">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<main class="content p-4">
|
||||||
|
<h2>Submit a New Link</h2>
|
||||||
|
|
||||||
|
<?php if (!empty($errors)): ?>
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<?php foreach ($errors as $error): ?><p class="mb-0"><?php echo $error; ?></p><?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<p class="mb-0">Thank you for your submission! It will be reviewed shortly.</p>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form action="submit.php" method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="title" class="form-label">Title</label>
|
||||||
|
<input type="text" class="form-control" id="title" name="title" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="url" class="form-label">URL</label>
|
||||||
|
<input type="url" class="form-control" id="url" name="url" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="category" class="form-label">Category</label>
|
||||||
|
<select class="form-select" id="category" name="category">
|
||||||
|
<option selected disabled>-- Select a Category --</option>
|
||||||
|
<?php foreach($categories as $cat): ?>
|
||||||
|
<option value="<?php echo $cat['id']; ?>"><?php echo htmlspecialchars($cat['name']); ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="subcategory_id" class="form-label">Subcategory</label>
|
||||||
|
<select class="form-select" id="subcategory_id" name="subcategory_id" required>
|
||||||
|
<option selected disabled>-- Select a Subcategory --</option>
|
||||||
|
<?php foreach($subcategories as $cat_id => $subs): ?>
|
||||||
|
<?php foreach($subs as $sub): ?>
|
||||||
|
<option class="d-none" data-category="<?php echo $cat_id; ?>" value="<?php echo $sub['id']; ?>"><?php echo htmlspecialchars($sub['name']); ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="description" class="form-label">Description</label>
|
||||||
|
<textarea class="form-control" id="description" name="description" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit Link</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('category').addEventListener('change', function() {
|
||||||
|
const categoryId = this.value;
|
||||||
|
const subcategorySelect = document.getElementById('subcategory_id');
|
||||||
|
|
||||||
|
// Reset and show the default option
|
||||||
|
subcategorySelect.value = '-- Select a Subcategory --';
|
||||||
|
|
||||||
|
// Hide all subcategory options
|
||||||
|
Array.from(subcategorySelect.options).forEach(opt => {
|
||||||
|
if (opt.dataset.category) { // Skip the default disabled option
|
||||||
|
opt.classList.add('d-none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show subcategories for the selected category
|
||||||
|
const relevantOptions = subcategorySelect.querySelectorAll(`[data-category="${categoryId}"]`);
|
||||||
|
relevantOptions.forEach(opt => opt.classList.remove('d-none'));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user