377 lines
16 KiB
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>
|