Auto commit: 2025-12-17T23:56:00.480Z

This commit is contained in:
Flatlogic Bot 2025-12-17 23:56:00 +00:00
parent dfb123b21a
commit 0ba5997fad
13 changed files with 1401 additions and 143 deletions

155
admin/categories.php Normal file
View 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>&copy; <?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
View 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>&copy; <?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
View 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>&copy; <?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
View 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>&copy; <?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
View 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
View File

@ -0,0 +1 @@
// Future javascript for interactivity

View 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
View 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

299
index.php
View File

@ -1,150 +1,169 @@
<?php
declare(strict_types=1);
@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>
<?php session_start(); ?>
<!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($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?></title>
<meta name="description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'A public web directory of curated links.'); ?>">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="<?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>">
<meta property="og:description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'A public web directory of curated links.'); ?>">
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:title" content="<?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>">
<meta property="twitter:description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'A public web directory of curated links.'); ?>">
<meta property="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
<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>
<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>
<?php
require_once __DIR__ . '/db/config.php';
$pdo = db();
// Fetch visible categories from the database
$stmt = $pdo->query("SELECT * FROM categories WHERE visibility = 1 ORDER BY display_order ASC, name ASC");
$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>
<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>
</header>
<div class="container my-4">
<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>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</div>
</div>
</div>
<footer class="footer">
<p>&copy; <?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>
</html>

100
login.php Normal file
View 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>&copy; <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
</footer>
</body>
</html>

6
logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_unset();
session_destroy();
header("Location: index.php");
exit;

107
register.php Normal file
View 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>&copy; <?php echo date("Y"); ?> <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Web Directory'); ?>. All Rights Reserved.</p>
</footer>
</body>
</html>

160
submit.php Normal file
View 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>&copy; <?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>