-
Notifications
You must be signed in to change notification settings - Fork 0
Build fullstack NightFlix video site (React + Express + MongoDB) #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| node_modules | ||
| .env | ||
| backend/node_modules | ||
| frontend/node_modules | ||
| dist | ||
| coverage | ||
| .DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,64 @@ | ||
| # Decrypt | ||
| ### [+] Created By HTR-TECH (@***tahmid.rayat***) | ||
| ### [+] Disclaimer : | ||
| ***Decrypter is a tool to decrypt Encrypted Bash Scripts into a Readable Format.This Tool is created for Educational Purpose only.I am not responsible for any misuse of this tool.*** | ||
| # NightFlix PHP Video Website | ||
|
|
||
| <img src="https://raw.githubusercontent.com/htr-tech/release-download/master/images/decrypter.png" alt="" border="0" /> | ||
| A shared-hosting-friendly video website built with pure PHP, MySQL, HTML, CSS, and JavaScript. It is structured for cPanel deployment and does not require Node.js. | ||
|
|
||
| ### [+] Installation | ||
| ```apt update``` | ||
| ## Folder structure | ||
|
|
||
| ```apt install git python2 -y``` | ||
| - `public_html/` - public pages (`index.php`, `video.php`, `upload.php`, `search.php`) | ||
| - `admin/` - session-based admin dashboard and CRUD pages | ||
| - `assets/` - CSS and JavaScript assets | ||
| - `includes/` - configuration, helpers, shared header, shared footer | ||
| - `uploads/` - reserved folder for future local file uploads | ||
| - `database.sql` - MySQL schema and seed data | ||
|
|
||
| ```git clone https://github.com/hax0rtahm1d/decrypt``` | ||
| ## Features | ||
|
|
||
| ```cd decrypt``` | ||
| ### Public site | ||
| - Responsive dark mode homepage with YouTube-style video grid | ||
| - Search by title or tags | ||
| - Category and tag filters | ||
| - Video detail page with MP4 or iframe embed support | ||
| - View counter and related videos | ||
| - Comment system | ||
| - Upload form | ||
| - Popup ad (once per session), banner ads, and announcement area | ||
|
|
||
| ```python2 dec.py``` | ||
| ### Admin panel | ||
| - Session-based login | ||
| - Dashboard with total videos, total views, and daily/monthly statistics tables | ||
| - Video CRUD | ||
| - Category CRUD | ||
| - Tag CRUD | ||
| - Ads CRUD with enable/disable | ||
| - Announcement edit/enable toggle | ||
| - Site settings for logo, primary color, and popup ads | ||
|
|
||
| ### Or, Use Single Command | ||
| ## Default admin account | ||
|
|
||
| ``` | ||
| apt update && apt install git python2 -y && git clone https://github.com/hax0rtahm1d/decrypt && cd decrypt && python2 dec.py | ||
| ``` | ||
| - Username: `admin` | ||
| - Password: `admin123` | ||
|
|
||
| ## [+] Find Me on : | ||
| [](https://github.com/htr-tech) | ||
| [](https://www.instagram.com/tahmid.rayat) | ||
| [](https://m.me/tahmid.rayat.official) | ||
| ## Deploy on cPanel / shared hosting | ||
|
|
||
| 1. Create a MySQL database and user in cPanel. | ||
| 2. Import `database.sql` using phpMyAdmin. | ||
| 3. Upload the project files so that these folders exist in your hosting account: | ||
| - `public_html/` | ||
| - `admin/` | ||
| - `assets/` | ||
| - `includes/` | ||
| - `uploads/` | ||
| 4. Edit `includes/config.php` and update: | ||
| - `DB_HOST` | ||
| - `DB_NAME` | ||
| - `DB_USER` | ||
| - `DB_PASS` | ||
| - `BASE_URL` if your site is installed in a subdirectory | ||
| 5. Open `public_html/index.php` in the browser. | ||
| 6. Open `admin/login.php` to access the admin dashboard. | ||
|
|
||
| ## Notes | ||
|
|
||
| - This project uses PDO and PHP sessions, which are supported on standard cPanel hosting. | ||
| - The popup ad is controlled by JavaScript and `sessionStorage`. | ||
| - `uploads/` is included for future file upload storage if you later switch from URL-based media to hosted files. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../includes/functions.php'; | ||
| require_admin(); | ||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
| if (!empty($_POST['id'])) { | ||
| $stmt = $pdo->prepare('UPDATE ads SET image=:image, link=:link, position=:position, active=:active WHERE id=:id'); | ||
| $stmt->execute(['image' => trim($_POST['image']), 'link' => trim($_POST['link']), 'position' => trim($_POST['position']), 'active' => !empty($_POST['active']) ? 1 : 0, 'id' => (int) $_POST['id']]); | ||
| } else { | ||
| $stmt = $pdo->prepare('INSERT INTO ads (image, link, position, active) VALUES (:image, :link, :position, :active)'); | ||
| $stmt->execute(['image' => trim($_POST['image']), 'link' => trim($_POST['link']), 'position' => trim($_POST['position']), 'active' => !empty($_POST['active']) ? 1 : 0]); | ||
| } | ||
| header('Location: ads.php'); exit; | ||
| } | ||
| if (isset($_GET['delete'])) { $pdo->prepare('DELETE FROM ads WHERE id=:id')->execute(['id' => (int) $_GET['delete']]); header('Location: ads.php'); exit; } | ||
|
Comment on lines
+4
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make ad mutations POST-only and CSRF-protected. The delete branch can be triggered by a simple cross-site navigation, prefetch, or crawler while an admin session is active, and the POST write path has no token check either. Use POST for deletion and validate a CSRF token for every mutation. Also applies to: 19-19 🤖 Prompt for AI Agents |
||
| $editing = null; | ||
| if (isset($_GET['edit'])) { $editing = $pdo->prepare('SELECT * FROM ads WHERE id=:id'); $editing->execute(['id' => (int) $_GET['edit']]); $editing = $editing->fetch(); } | ||
| $ads = get_ads(null, false); | ||
| ?> | ||
| <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Ads</title><link rel="stylesheet" href="../assets/css/style.css"></head><body><div class="admin-layout"><?php include __DIR__ . '/partials/sidebar.php'; ?><main class="admin-main"><section class="panel"><h1>Ads</h1><form method="post" class="admin-form"><?php if ($editing): ?><input type="hidden" name="id" value="<?php echo (int) $editing['id']; ?>"><?php endif; ?><label>Image URL<input name="image" value="<?php echo esc($editing['image'] ?? ''); ?>" required></label><label>Link<input name="link" value="<?php echo esc($editing['link'] ?? ''); ?>" required></label><label>Position<select name="position"><?php foreach (['popup','header','middle','footer'] as $position): ?><option value="<?php echo $position; ?>" <?php echo (($editing['position'] ?? '') === $position) ? 'selected' : ''; ?>><?php echo ucfirst($position); ?></option><?php endforeach; ?></select></label><label class="inline-check"><input type="checkbox" name="active" value="1" <?php echo !isset($editing['active']) || !empty($editing['active']) ? 'checked' : ''; ?>> Active</label><button class="btn-primary" type="submit"><?php echo $editing ? 'Update' : 'Create'; ?> Ad</button></form></section><section class="panel"><table class="data-table"><tr><th>Position</th><th>Active</th><th>Actions</th></tr><?php foreach ($ads as $ad): ?><tr><td><?php echo esc($ad['position']); ?></td><td><?php echo $ad['active'] ? 'Yes' : 'No'; ?></td><td><a href="?edit=<?php echo (int) $ad['id']; ?>">Edit</a> | <a href="?delete=<?php echo (int) $ad['id']; ?>">Delete</a></td></tr><?php endforeach; ?></table></section></main></div></body></html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../includes/functions.php'; | ||
| require_admin(); | ||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
| $current = $pdo->query('SELECT id FROM announcements ORDER BY id DESC LIMIT 1')->fetchColumn(); | ||
| if ($current) { | ||
| $stmt = $pdo->prepare('UPDATE announcements SET title=:title, content=:content, active=:active WHERE id=:id'); | ||
| $stmt->execute(['title' => trim($_POST['title']), 'content' => trim($_POST['content']), 'active' => !empty($_POST['active']) ? 1 : 0, 'id' => $current]); | ||
| } else { | ||
| $stmt = $pdo->prepare('INSERT INTO announcements (title, content, active) VALUES (:title, :content, :active)'); | ||
| $stmt->execute(['title' => trim($_POST['title']), 'content' => trim($_POST['content']), 'active' => !empty($_POST['active']) ? 1 : 0]); | ||
| } | ||
| header('Location: announcement.php'); exit; | ||
|
Comment on lines
+4
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Protect announcement writes with CSRF validation. This endpoint changes admin-owned state on any authenticated POST, and the form does not submit a token. Add a per-session/per-form CSRF token and reject writes when it is missing or invalid. Also applies to: 17-17 🤖 Prompt for AI Agents |
||
| } | ||
| $announcement = $pdo->query('SELECT * FROM announcements ORDER BY id DESC LIMIT 1')->fetch(); | ||
| ?> | ||
| <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Announcement</title><link rel="stylesheet" href="../assets/css/style.css"></head><body><div class="admin-layout"><?php include __DIR__ . '/partials/sidebar.php'; ?><main class="admin-main"><section class="panel"><h1>Announcement</h1><form method="post" class="admin-form"><label>Title<input name="title" value="<?php echo esc($announcement['title'] ?? ''); ?>" required></label><label>Content<textarea name="content" rows="5" required><?php echo esc($announcement['content'] ?? ''); ?></textarea></label><label class="inline-check"><input type="checkbox" name="active" value="1" <?php echo !isset($announcement['active']) || !empty($announcement['active']) ? 'checked' : ''; ?>> Active</label><button class="btn-primary" type="submit">Save Announcement</button></form></section></main></div></body></html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../includes/functions.php'; | ||
| require_admin(); | ||
| $stats = dashboard_stats(); | ||
| $videos = fetch_videos(); | ||
| $categories = get_categories(); | ||
| $tags = get_tags(); | ||
| $ads = get_ads(null, false); | ||
| $announcement = get_announcement(); | ||
| $settings = site_settings(); | ||
| ?> | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>Admin Dashboard</title> | ||
| <link rel="stylesheet" href="../assets/css/style.css"> | ||
| </head> | ||
| <body> | ||
| <div class="admin-layout"> | ||
| <aside class="admin-sidebar panel"> | ||
| <h2>Admin Panel</h2> | ||
| <p class="muted">Logged in as <?php echo esc($_SESSION['admin_username']); ?></p> | ||
| <nav class="admin-nav"> | ||
| <a href="index.php">Dashboard</a> | ||
| <a href="videos.php">Videos</a> | ||
| <a href="taxonomy.php">Categories & Tags</a> | ||
| <a href="ads.php">Ads</a> | ||
| <a href="announcement.php">Announcement</a> | ||
| <a href="settings.php">Settings</a> | ||
| <a href="logout.php">Logout</a> | ||
| </nav> | ||
| </aside> | ||
| <main class="admin-main"> | ||
| <section class="stats-grid"> | ||
| <div class="panel stat-card"><span>Total Videos</span><strong><?php echo $stats['total_videos']; ?></strong></div> | ||
| <div class="panel stat-card"><span>Total Views</span><strong><?php echo $stats['total_views']; ?></strong></div> | ||
| </section> | ||
| <section class="panel"> | ||
| <h2>Daily Views</h2> | ||
| <table class="data-table"><tr><th>Date</th><th>Views</th></tr><?php foreach ($stats['daily'] as $row): ?><tr><td><?php echo esc($row['label']); ?></td><td><?php echo (int) $row['count']; ?></td></tr><?php endforeach; ?></table> | ||
| </section> | ||
| <section class="panel"> | ||
| <h2>Monthly Views</h2> | ||
| <table class="data-table"><tr><th>Month</th><th>Views</th></tr><?php foreach ($stats['monthly'] as $row): ?><tr><td><?php echo esc($row['label']); ?></td><td><?php echo (int) $row['count']; ?></td></tr><?php endforeach; ?></table> | ||
| </section> | ||
| <section class="panel"> | ||
| <h2>Quick Overview</h2> | ||
| <p class="muted">Videos: <?php echo count($videos); ?> · Categories: <?php echo count($categories); ?> · Tags: <?php echo count($tags); ?> · Ads: <?php echo count($ads); ?> · Announcement: <?php echo $announcement ? 'Active' : 'None'; ?> · Primary color: <?php echo esc($settings['primary_color']); ?></p> | ||
| </section> | ||
| </main> | ||
| </div> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../includes/functions.php'; | ||
| if (!empty($_SESSION['admin_id'])) { | ||
| header('Location: index.php'); | ||
| exit; | ||
| } | ||
| $error = ''; | ||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
| if (admin_login(trim($_POST['username']), $_POST['password'])) { | ||
| header('Location: index.php'); | ||
| exit; | ||
| } | ||
| $error = 'Invalid login credentials.'; | ||
| } | ||
| ?> | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>Admin Login</title> | ||
| <link rel="stylesheet" href="../assets/css/style.css"> | ||
| </head> | ||
| <body> | ||
| <div class="login-shell"> | ||
| <form method="post" class="panel login-card admin-form"> | ||
| <h1>Admin Login</h1> | ||
| <?php if ($error): ?><p class="error"><?php echo esc($error); ?></p><?php endif; ?> | ||
| <label>Username<input type="text" name="username" required></label> | ||
| <label>Password<input type="password" name="password" required></label> | ||
| <button type="submit" class="btn-primary">Sign In</button> | ||
| <p class="muted">Default admin is created in <code>database.sql</code>.</p> | ||
| </form> | ||
| </div> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../includes/config.php'; | ||
| session_destroy(); | ||
| header('Location: login.php'); | ||
| exit; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <aside class="admin-sidebar panel"> | ||
| <h2>Admin Panel</h2> | ||
| <p class="muted">Logged in as <?php echo esc($_SESSION['admin_username']); ?></p> | ||
| <nav class="admin-nav"> | ||
| <a href="index.php">Dashboard</a> | ||
| <a href="videos.php">Videos</a> | ||
| <a href="taxonomy.php">Categories & Tags</a> | ||
| <a href="ads.php">Ads</a> | ||
| <a href="announcement.php">Announcement</a> | ||
| <a href="settings.php">Settings</a> | ||
| <a href="logout.php">Logout</a> | ||
| </nav> | ||
| </aside> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../includes/functions.php'; | ||
| require_admin(); | ||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
| $current = site_settings(); | ||
| $stmt = $pdo->prepare('UPDATE settings SET site_name=:site_name, logo=:logo, primary_color=:primary_color, popup_ads_enabled=:popup_ads_enabled WHERE id=:id'); | ||
| $stmt->execute(['site_name' => trim($_POST['site_name']), 'logo' => trim($_POST['logo']), 'primary_color' => trim($_POST['primary_color']), 'popup_ads_enabled' => !empty($_POST['popup_ads_enabled']) ? 1 : 0, 'id' => $current['id']]); | ||
| header('Location: settings.php'); exit; | ||
| } | ||
| $settings = site_settings(); | ||
| ?> | ||
| <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Settings</title><link rel="stylesheet" href="../assets/css/style.css"></head><body><div class="admin-layout"><?php include __DIR__ . '/partials/sidebar.php'; ?><main class="admin-main"><section class="panel"><h1>Settings</h1><form method="post" class="admin-form"><label>Site Name<input name="site_name" value="<?php echo esc($settings['site_name']); ?>" required></label><label>Logo URL<input name="logo" value="<?php echo esc($settings['logo']); ?>"></label><label>Primary Color<input name="primary_color" value="<?php echo esc($settings['primary_color']); ?>" required></label><label class="inline-check"><input type="checkbox" name="popup_ads_enabled" value="1" <?php echo !empty($settings['popup_ads_enabled']) ? 'checked' : ''; ?>> Enable Popup Ads</label><button class="btn-primary" type="submit">Save Settings</button></form></section></main></div></body></html> |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,43 @@ | ||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||
| require_once __DIR__ . '/../includes/functions.php'; | ||||||||||||||||||||||||||
| require_admin(); | ||||||||||||||||||||||||||
| $editCategory = null; | ||||||||||||||||||||||||||
| $editTag = null; | ||||||||||||||||||||||||||
| if (isset($_GET['edit_category'])) { | ||||||||||||||||||||||||||
| $stmt = $pdo->prepare('SELECT * FROM categories WHERE id=:id'); | ||||||||||||||||||||||||||
| $stmt->execute(['id' => (int) $_GET['edit_category']]); | ||||||||||||||||||||||||||
| $editCategory = $stmt->fetch(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (isset($_GET['edit_tag'])) { | ||||||||||||||||||||||||||
| $stmt = $pdo->prepare('SELECT * FROM tags WHERE id=:id'); | ||||||||||||||||||||||||||
| $stmt->execute(['id' => (int) $_GET['edit_tag']]); | ||||||||||||||||||||||||||
| $editTag = $stmt->fetch(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||||||||||||||||||||||||||
| if ($_POST['entity'] === 'category') { | ||||||||||||||||||||||||||
| if (!empty($_POST['id'])) { | ||||||||||||||||||||||||||
| $stmt = $pdo->prepare('UPDATE categories SET name=:name, description=:description WHERE id=:id'); | ||||||||||||||||||||||||||
| $stmt->execute(['name' => trim($_POST['name']), 'description' => trim($_POST['description']), 'id' => (int) $_POST['id']]); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| $stmt = $pdo->prepare('INSERT INTO categories (name, description) VALUES (:name, :description)'); | ||||||||||||||||||||||||||
| $stmt->execute(['name' => trim($_POST['name']), 'description' => trim($_POST['description'])]); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if ($_POST['entity'] === 'tag') { | ||||||||||||||||||||||||||
| if (!empty($_POST['id'])) { | ||||||||||||||||||||||||||
| $stmt = $pdo->prepare('UPDATE tags SET name=:name WHERE id=:id'); | ||||||||||||||||||||||||||
| $stmt->execute(['name' => trim($_POST['name']), 'id' => (int) $_POST['id']]); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| $stmt = $pdo->prepare('INSERT INTO tags (name) VALUES (:name)'); | ||||||||||||||||||||||||||
| $stmt->execute(['name' => trim($_POST['name'])]); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| header('Location: taxonomy.php'); | ||||||||||||||||||||||||||
| exit; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+16
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding CSRF token validation for form submissions. The POST handlers for creating/updating categories and tags lack CSRF protection. While less severe than GET-based mutations, this still allows cross-site request forgery attacks. Consider generating a session-bound CSRF token and validating it on form submission. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| if (isset($_GET['delete_category'])) { $pdo->prepare('DELETE FROM categories WHERE id=:id')->execute(['id' => (int) $_GET['delete_category']]); header('Location: taxonomy.php'); exit; } | ||||||||||||||||||||||||||
| if (isset($_GET['delete_tag'])) { $pdo->prepare('DELETE FROM tags WHERE id=:id')->execute(['id' => (int) $_GET['delete_tag']]); header('Location: taxonomy.php'); exit; } | ||||||||||||||||||||||||||
|
Comment on lines
+38
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DELETE operations via GET are unsafe and vulnerable to CSRF. Using GET requests for destructive operations violates HTTP semantics and creates significant security risks:
Convert these to POST requests with CSRF token protection. 🔒 Proposed fix: Use POST with confirmation-if (isset($_GET['delete_category'])) { $pdo->prepare('DELETE FROM categories WHERE id=:id')->execute(['id' => (int) $_GET['delete_category']]); header('Location: taxonomy.php'); exit; }
-if (isset($_GET['delete_tag'])) { $pdo->prepare('DELETE FROM tags WHERE id=:id')->execute(['id' => (int) $_GET['delete_tag']]); header('Location: taxonomy.php'); exit; }
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_category'])) {
+ $pdo->prepare('DELETE FROM categories WHERE id=:id')->execute(['id' => (int) $_POST['delete_category']]);
+ header('Location: taxonomy.php');
+ exit;
+}
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_tag'])) {
+ $pdo->prepare('DELETE FROM tags WHERE id=:id')->execute(['id' => (int) $_POST['delete_tag']]);
+ header('Location: taxonomy.php');
+ exit;
+}Then update the delete links in the HTML to be forms: <form method="post" style="display:inline">
<input type="hidden" name="delete_category" value="<?php echo (int) $category['id']; ?>">
<button type="submit" onclick="return confirm('Delete this category?')">Delete</button>
</form>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| $categories = get_categories(); | ||||||||||||||||||||||||||
| $tags = get_tags(); | ||||||||||||||||||||||||||
| ?> | ||||||||||||||||||||||||||
| <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Taxonomy</title><link rel="stylesheet" href="../assets/css/style.css"></head><body><div class="admin-layout"><?php include __DIR__ . '/partials/sidebar.php'; ?><main class="admin-main"><section class="panel split-panels"><div><h1>Categories</h1><form method="post" class="admin-form"><input type="hidden" name="entity" value="category"><?php if ($editCategory): ?><input type="hidden" name="id" value="<?php echo (int) $editCategory['id']; ?>"><?php endif; ?><label>Name<input name="name" value="<?php echo esc($editCategory['name'] ?? ''); ?>" required></label><label>Description<textarea name="description" rows="3"><?php echo esc($editCategory['description'] ?? ''); ?></textarea></label><button class="btn-primary" type="submit"><?php echo $editCategory ? 'Update' : 'Save'; ?> Category</button></form><table class="data-table"><tr><th>Name</th><th>Description</th><th>Action</th></tr><?php foreach ($categories as $category): ?><tr><td><?php echo esc($category['name']); ?></td><td><?php echo esc($category['description']); ?></td><td><a href="?edit_category=<?php echo (int) $category['id']; ?>">Edit</a> | <a href="?delete_category=<?php echo (int) $category['id']; ?>">Delete</a></td></tr><?php endforeach; ?></table></div><div><h1>Tags</h1><form method="post" class="admin-form"><input type="hidden" name="entity" value="tag"><?php if ($editTag): ?><input type="hidden" name="id" value="<?php echo (int) $editTag['id']; ?>"><?php endif; ?><label>Name<input name="name" value="<?php echo esc($editTag['name'] ?? ''); ?>" required></label><button class="btn-primary" type="submit"><?php echo $editTag ? 'Update' : 'Save'; ?> Tag</button></form><table class="data-table"><tr><th>Name</th><th>Action</th></tr><?php foreach ($tags as $tag): ?><tr><td><?php echo esc($tag['name']); ?></td><td><a href="?edit_tag=<?php echo (int) $tag['id']; ?>">Edit</a> | <a href="?delete_tag=<?php echo (int) $tag['id']; ?>">Delete</a></td></tr><?php endforeach; ?></table></div></section></main></div></body></html> | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate
position,link, andimagebefore saving.A tampered request can still send any
positionand any URI scheme here.database.sql:46-53only rejects bad positions after the DB round-trip, and the stored ad URLs are later rendered directly into publichref/srcattributes inincludes/header.php:40-41andincludes/footer.php:2-5,12. Enforce an allow-list forpositionand restrict URLs to safe schemes such ashttp/https.🤖 Prompt for AI Agents