Files
rss-catcher/index.php

377 lines
16 KiB
PHP

<?php
require 'config.php';
// --- ACTION HANDLERS ---
// 1. Add Feed
if (isset($_POST['add_feed'])) {
$stmt = $pdo->prepare("INSERT INTO feeds (site_name, rss_url, group_id) VALUES (?, ?, ?)");
$stmt->execute([$_POST['site_name'], $_POST['rss_url'], $_POST['group_id']]);
header("Location: index.php");
exit;
}
// 2. Add Group
if (isset($_POST['add_group'])) {
$stmt = $pdo->prepare("INSERT INTO `groups` (name) VALUES (?)");
$stmt->execute([$_POST['group_name']]);
header("Location: index.php");
exit;
}
// 3. Delete Feed
if (isset($_POST['delete_feed'])) {
$stmt = $pdo->prepare("DELETE FROM feeds WHERE id = ?");
$stmt->execute([$_POST['feed_id_to_delete']]);
header("Location: index.php");
exit;
}
// 4. Edit Feed
if (isset($_POST['edit_feed'])) {
$stmt = $pdo->prepare("UPDATE feeds SET site_name = ?, rss_url = ?, group_id = ? WHERE id = ?");
$stmt->execute([
$_POST['edit_site_name'],
$_POST['edit_rss_url'],
$_POST['edit_group_id'],
$_POST['edit_feed_id']
]);
header("Location: index.php");
exit;
}
// 5. Mark as Read (Updated with Undo Memory)
if (isset($_GET['mark_read'])) {
$id = $_GET['mark_read'];
// Update DB
$stmt = $pdo->prepare("UPDATE items SET is_read = 1, is_saved = 0 WHERE id = ?");
$stmt->execute([$id]);
// SAVE ID TO SESSION FOR UNDO
$_SESSION['undo_entry_id'] = $id;
// Redirect logic (Same as before)
$params = [];
if (isset($_GET['view'])) $params[] = "view=" . $_GET['view'];
if (isset($_GET['group_id'])) $params[] = "group_id=" . $_GET['group_id'];
$redirect = count($params) > 0 ? "?" . implode("&", $params) : "index.php";
header("Location: $redirect");
exit;
}
// 6. Save/Unsave Logic
if (isset($_GET['save_item'])) {
$stmt = $pdo->prepare("UPDATE items SET is_saved = 1 WHERE id = ?");
$stmt->execute([$_GET['save_item']]);
$params = [];
if (isset($_GET['view'])) $params[] = "view=" . $_GET['view'];
if (isset($_GET['group_id'])) $params[] = "group_id=" . $_GET['group_id'];
$redirect = count($params) > 0 ? "?" . implode("&", $params) : "index.php";
header("Location: $redirect");
exit;
}
if (isset($_GET['unsave_item'])) {
$stmt = $pdo->prepare("UPDATE items SET is_saved = 0 WHERE id = ?");
$stmt->execute([$_GET['unsave_item']]);
$params = [];
$params[] = "view=" . ($_GET['view'] ?? 'saved');
if (isset($_GET['group_id'])) $params[] = "group_id=" . $_GET['group_id'];
$redirect = "?" . implode("&", $params);
header("Location: $redirect");
exit;
}
// 7. NEW: Undo Handler
if (isset($_GET['undo_action'])) {
if (isset($_SESSION['undo_entry_id'])) {
// Revert the last item to Unread (is_read = 0)
$stmt = $pdo->prepare("UPDATE items SET is_read = 0 WHERE id = ?");
$stmt->execute([$_SESSION['undo_entry_id']]);
// Clear the session so the button disappears
unset($_SESSION['undo_entry_id']);
}
// Redirect logic to keep you on the same page
$params = [];
if (isset($_GET['view'])) $params[] = "view=" . $_GET['view'];
if (isset($_GET['group_id'])) $params[] = "group_id=" . $_GET['group_id'];
$redirect = count($params) > 0 ? "?" . implode("&", $params) : "index.php";
header("Location: $redirect");
exit;
}
// --- DATA FETCHING ---
$groups = $pdo->query("SELECT * FROM `groups`")->fetchAll();
// Fetch all feeds for the management modal
$all_feeds = $pdo->query("SELECT feeds.*, groups.name as group_name FROM feeds LEFT JOIN `groups` ON feeds.group_id = groups.id ORDER BY site_name ASC")->fetchAll();
$view_mode = $_GET['view'] ?? 'unread';
$filter_group_id = $_GET['group_id'] ?? null;
// Build SQL
$sql = "SELECT items.id AS item_id, items.title, items.link, items.description, items.pub_date, feeds.site_name
FROM items JOIN feeds ON items.feed_id = feeds.id ";
$params = [];
if ($view_mode === 'saved') {
$sql .= "WHERE is_saved = 1 ";
} else {
$sql .= "WHERE is_read = 0 AND is_saved = 0 ";
}
if ($filter_group_id) {
$sql .= "AND feeds.group_id = ? ";
$params[] = $filter_group_id;
}
$sql .= "ORDER BY pub_date DESC LIMIT 50";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$items = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RSS Catcher</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.sidebar { height: 100vh; overflow-y: auto; position: fixed; width: 25%; }
.main-content { margin-left: 25%; width: 75%; }
.nav-link.active { background-color: #0d6efd; color: white !important; }
</style>
</head>
<body class="bg-light">
<div class="container-fluid">
<div class="row">
<div class="col-md-3 bg-white p-4 border-end sidebar">
<h3 class="mb-4">RSS Catcher</h3>
<div class="list-group mb-4">
<a href="index.php" class="list-group-item list-group-item-action <?= ($view_mode == 'unread' && !$filter_group_id) ? 'active' : '' ?>">
📥 All Unread
</a>
<a href="?view=saved" class="list-group-item list-group-item-action <?= ($view_mode == 'saved') ? 'active' : '' ?>">
⭐ Saved for Later
</a>
</div>
<h6 class="text-muted text-uppercase small">Filter by Group</h6>
<div class="list-group mb-4">
<?php foreach($groups as $g): ?>
<a href="?group_id=<?= $g['id'] ?>"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center <?= ($filter_group_id == $g['id']) ? 'active' : '' ?>">
<?= htmlspecialchars($g['name']) ?>
</a>
<?php endforeach; ?>
</div>
<hr>
<div class="d-grid gap-2">
<button class="btn btn-outline-dark btn-sm" data-bs-toggle="modal" data-bs-target="#manageFeedsModal">
🛠 Manage Feeds (Edit/Delete)
</button>
<div class="accordion" id="adminControls">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#addControls">
⚙️ Add New
</button>
</h2>
<div id="addControls" class="accordion-collapse collapse" data-bs-parent="#adminControls">
<div class="accordion-body">
<form method="post" class="mb-3">
<label class="form-label small">New Group</label>
<div class="input-group input-group-sm">
<input type="text" name="group_name" class="form-control" required>
<button class="btn btn-secondary" name="add_group">+</button>
</div>
</form>
<form method="post">
<label class="form-label small">New Feed</label>
<input type="text" name="site_name" class="form-control form-control-sm mb-2" placeholder="Site Name" required>
<input type="url" name="rss_url" class="form-control form-control-sm mb-2" placeholder="RSS URL" required>
<select name="group_id" class="form-select form-select-sm mb-2">
<?php foreach($groups as $g): ?>
<option value="<?= $g['id'] ?>"><?= $g['name'] ?></option>
<?php endforeach; ?>
</select>
<button class="btn btn-primary btn-sm w-100" name="add_feed">Add Feed</button>
</form>
</div>
</div>
</div>
</div>
<a href="fetch.php" class="btn btn-success btn-sm">Force Refresh</a>
</div>
</div>
<div class="col-md-9 p-4 main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>
<?php
$count_label = " <small class='text-muted'>(" . count($items) . ")</small>";
if ($view_mode === 'saved') echo "⭐ Saved Articles" . $count_label;
elseif ($filter_group_id) echo "📂 Group View" . $count_label;
else echo "📥 Unread Articles" . $count_label;
?>
</h2>
<?php if (isset($_SESSION['undo_entry_id'])): ?>
<div class="alert alert-warning d-flex justify-content-between align-items-center mb-3">
<span>✅ Article marked as read.</span>
<?php
$undoParams = "undo_action=true";
if (isset($_GET['view'])) $undoParams .= "&view=" . $_GET['view'];
if (isset($_GET['group_id'])) $undoParams .= "&group_id=" . $_GET['group_id'];
?>
<a href="?<?= $undoParams ?>" class="btn btn-sm btn-dark">↩ Undo</a>
</div>
<?php endif; ?>
</div>
<?php if (count($items) === 0): ?>
<div class="alert alert-info">No articles found in this view.</div>
<?php endif; ?>
<div class="row">
<?php foreach($items as $item): ?>
<div class="col-12 mb-3">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">
<a href="<?= $item['link'] ?>" target="_blank" class="text-decoration-none text-dark">
<?= htmlspecialchars($item['title']) ?>
</a>
</h5>
<h6 class="card-subtitle mb-2 text-muted small">
<?= htmlspecialchars($item['site_name']) ?> | <?= date('M j, g:i a', strtotime($item['pub_date'])) ?>
</h6>
<p class="card-text text-secondary">
<?= strip_tags(substr($item['description'], 0, 250)) ?>...
</p>
<div class="d-flex gap-2">
<?php
$contextParams = "&view=" . $view_mode;
if ($filter_group_id) $contextParams .= "&group_id=" . $filter_group_id;
?>
<a href="?mark_read=<?= $item['item_id'] ?><?= $contextParams ?>" class="btn btn-sm btn-outline-secondary">Mark as Read</a>
<?php if($view_mode === 'saved'): ?>
<a href="?unsave_item=<?= $item['item_id'] ?><?= $contextParams ?>" class="btn btn-sm btn-warning">Remove from Saved</a>
<?php else: ?>
<a href="?save_item=<?= $item['item_id'] ?><?= $contextParams ?>" class="btn btn-sm btn-outline-primary">⭐ Save for Later</a>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<div class="modal fade" id="manageFeedsModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Manage Feeds</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>Name</th>
<th>Group</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach($all_feeds as $f): ?>
<tr>
<td><?= htmlspecialchars($f['site_name']) ?></td>
<td><?= htmlspecialchars($f['group_name']) ?></td>
<td>
<button class="btn btn-sm btn-primary"
onclick="openEditModal(<?= $f['id'] ?>, '<?= addslashes($f['site_name']) ?>', '<?= addslashes($f['rss_url']) ?>', <?= $f['group_id'] ?>)">
Edit
</button>
<form method="post" style="display:inline;" onsubmit="return confirm('Delete this feed and ALL its articles?');">
<input type="hidden" name="feed_id_to_delete" value="<?= $f['id'] ?>">
<button class="btn btn-sm btn-danger" name="delete_feed">Delete</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" id="editFeedModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="post">
<div class="modal-header">
<h5 class="modal-title">Edit Feed</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="edit_feed_id" id="edit_feed_id">
<div class="mb-3">
<label>Site Name</label>
<input type="text" name="edit_site_name" id="edit_site_name" class="form-control" required>
</div>
<div class="mb-3">
<label>RSS URL</label>
<input type="url" name="edit_rss_url" id="edit_rss_url" class="form-control" required>
</div>
<div class="mb-3">
<label>Group</label>
<select name="edit_group_id" id="edit_group_id" class="form-select">
<?php foreach($groups as $g): ?>
<option value="<?= $g['id'] ?>"><?= $g['name'] ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" name="edit_feed">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function openEditModal(id, name, url, groupId) {
// Populate fields
document.getElementById('edit_feed_id').value = id;
document.getElementById('edit_site_name').value = name;
document.getElementById('edit_rss_url').value = url;
document.getElementById('edit_group_id').value = groupId;
// Open Modal
var myModal = new bootstrap.Modal(document.getElementById('editFeedModal'));
myModal.show();
}
</script>
</body>
</html>