Files
2025-12-01 21:16:37 +01:00

269 lines
12 KiB
PHP

<?php
$page_title = 'Bookmarkly';
require_once 'include/init.php'; // Use init.php first for DB connection
// Handle token login BEFORE auth check
if (isset($_GET['token'])) {
$token = $_GET['token'];
$stmt = $pdo->prepare("SELECT id FROM tabs WHERE login_token = ?");
$stmt->execute([$token]);
$tab = $stmt->fetch();
if ($tab) {
// Log in as a temporary guest for this tab
$_SESSION['guest_tab_id'] = $tab['id'];
// Redirect to the tab view without the token in the URL
header('Location: index.php?tab=' . $tab['id']);
exit;
} else {
// Invalid token, redirect to login or show an error
header('Location: login.php?error=invalid_token');
exit;
}
}
require_once 'include/auth.php';
// Fetch all settings for theme and layout
$stmt = $pdo->query("SELECT key, value FROM settings");
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
$open_in_new_tab = $settings['open_new_tab'] ?? '1';
$target_attr = $open_in_new_tab === '1' ? 'target="_blank"' : '';
// Get current user ID if logged in
$current_user_id = $_SESSION['user_id'] ?? null;
$current_user_role = $_SESSION['role'] ?? null;
$guest_tab_id = $_SESSION['guest_tab_id'] ?? null;
// Base query for fetching tabs
$tabs_stmt_sql = "SELECT DISTINCT t.* FROM tabs t ";
$params = [];
if ($guest_tab_id) {
// Guest user with token can only see the specific tab
$tabs_stmt_sql .= "WHERE t.id = :guest_tab_id";
$params[':guest_tab_id'] = $guest_tab_id;
} else if ($current_user_id) {
if ($current_user_role === 'admin') {
// Admin sees all tabs that are not explicitly hidden for them
$tabs_stmt_sql .= "
LEFT JOIN user_tab_visibility utv ON t.id = utv.tab_id AND utv.user_id = :current_user_id
WHERE utv.user_id IS NULL
";
} else { // 'user' and 'viewer' roles
// Users/Viewers see public tabs, their own tabs, or tabs they have access to,
// as long as they are not explicitly hidden.
$tabs_stmt_sql .= "
LEFT JOIN tab_viewer_access tva ON t.id = tva.tab_id
LEFT JOIN user_tab_visibility utv ON t.id = utv.tab_id AND utv.user_id = :current_user_id
WHERE utv.user_id IS NULL
AND (t.user_id IS NULL OR t.user_id = :current_user_id OR tva.user_id = :current_user_id)
";
}
$params[':current_user_id'] = $current_user_id;
} else {
// Guest sees only public (ownerless) tabs
$tabs_stmt_sql .= "WHERE t.user_id IS NULL";
}
$tabs_stmt_sql .= " ORDER BY t.sort_order, t.id";
$tabs_stmt = $pdo->prepare($tabs_stmt_sql);
$tabs_stmt->execute($params);
$tabs = $tabs_stmt->fetchAll();
// Determine the default tab to show.
// If the requested tab is not in the visible list, fall back to the first visible one.
$visible_tab_ids = array_column($tabs, 'id');
$current_tab_id = isset($_GET['tab']) ? (int)$_GET['tab'] : ($visible_tab_ids[0] ?? 1);
if (!in_array($current_tab_id, $visible_tab_ids)) {
$current_tab_id = $visible_tab_ids[0] ?? 1;
}
// Fetch details for the current tab to get background info
$current_tab_data = null;
if ($current_tab_id) {
$tab_details_stmt = $pdo->prepare("SELECT * FROM tabs WHERE id = :id");
$tab_details_stmt->execute([':id' => $current_tab_id]);
$current_tab_data = $tab_details_stmt->fetch();
}
// Get the ID of the 'FAVORITES' category (case-insensitive)
$fav_cat_stmt = $pdo->prepare("SELECT id FROM categories WHERE UPPER(name) = 'FAVORITES'");
$fav_cat_stmt->execute();
$favorites_cat_id = $fav_cat_stmt->fetchColumn();
// Fetch favorite bookmarks from the 'FAVORITES' category for the current tab
$fav_stmt = $pdo->prepare("
SELECT b.*
FROM bookmarks b
JOIN categories c ON b.category_id = c.id
WHERE b.is_favorite = 1 AND c.tab_id = :tab_id
ORDER BY b.sort_order ASC, b.title ASC
");
$fav_stmt->execute([':tab_id' => $current_tab_id]);
$favorites = $fav_stmt->fetchAll();
// Fetch categories for the current tab, excluding 'FAVORITES'
$cat_stmt = $pdo->prepare("SELECT * FROM categories WHERE tab_id = :tab_id AND UPPER(name) != 'FAVORITES' ORDER BY sort_order ASC, name ASC");
$cat_stmt->execute([':tab_id' => $current_tab_id]);
$categories = $cat_stmt->fetchAll();
// Fetch bookmarks for each category
$bookmarks_by_cat = [];
if ($categories) {
$bookmarks_stmt = $pdo->prepare("SELECT * FROM bookmarks WHERE category_id = :cat_id AND (is_favorite IS NULL OR is_favorite = 0) ORDER BY sort_order ASC, title ASC");
foreach ($categories as $category) {
$bookmarks_stmt->execute([':cat_id' => $category['id']]);
$bookmarks_by_cat[$category['id']] = $bookmarks_stmt->fetchAll();
}
}
require_once 'include/header.php';
?>
<div class="container mt-4">
<?php if (count($tabs) > 1): ?>
<ul class="nav nav-tabs" id="tabs-container">
<?php foreach ($tabs as $tab): ?>
<li class="nav-item" role="presentation" data-tab-id="<?php echo $tab['id']; ?>">
<a class="nav-link <?php if ($tab['id'] == $current_tab_id) echo 'active'; ?>" href="?tab=<?php echo $tab['id']; ?>">
<?php echo htmlspecialchars($tab['name'] === 'Default' ? translate('tab_default_name') : $tab['name']); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="tab-content mt-3">
<!-- Favorites -->
<?php if (!empty($favorites)): ?>
<div class="category-column mb-5">
<h5 class="mb-3"><?php echo translate('index_favorites'); ?></h5>
<div class="row">
<?php foreach ($favorites as $bookmark): ?>
<?php
// Clean up URL for display
$display_url = preg_replace('/^https?:\/\/(www\.)?/', '', $bookmark['url']);
$display_url = rtrim($display_url, '/');
// Build icon source URL
$icon_src = 'include/images/default-icon.png'; // Default icon
if (!empty($bookmark['icon'])) {
if (file_exists($bookmark['icon']) && strpos($bookmark['icon'], 'data/images/custom/') === 0) {
$icon_src = htmlspecialchars($bookmark['icon']);
} elseif (filter_var($bookmark['icon'], FILTER_VALIDATE_URL)) {
$icon_src = htmlspecialchars($bookmark['icon']);
} elseif (!empty($settings['favicon_extractor_url'])) {
$base_url = rtrim($settings['favicon_extractor_url'], '/');
$domain = htmlspecialchars($bookmark['icon']);
$icon_src = "{$base_url}/favicon/{$domain}?larger=true";
}
}
?>
<div class="col-lg-2 col-md-3 col-sm-4 col-6 mb-3">
<a href="<?php echo htmlspecialchars($bookmark['url']); ?>" <?php echo $target_attr; ?> class="btn d-flex align-items-center text-start p-2 bookmark-link">
<img src="<?php echo $icon_src; ?>" alt="" class="bookmark-favorite-icon me-3" loading="lazy" onerror="this.onerror=null;this.src='include/images/default-icon.png';">
<div>
<span><?php echo htmlspecialchars($bookmark['title']); ?></span>
<small class="d-block text-muted"><?php echo htmlspecialchars($display_url); ?></small>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- Categories and Bookmarks -->
<?php
$column_count = $current_tab_data['column_count'] ?? 5;
?>
<div class="row g-3 row-cols-1 row-cols-md-<?php echo htmlspecialchars($column_count); ?>">
<?php foreach ($categories as $category): ?>
<?php
$bookmarks = $bookmarks_by_cat[$category['id']] ?? [];
if (empty($bookmarks)) continue;
?>
<div class="col">
<div class="category-column">
<h5 class="mb-3"><?php echo htmlspecialchars($category['name']); ?></h5>
<div class="list-group list-group-flush">
<?php foreach ($bookmarks as $bookmark): ?>
<?php
// Build icon source URL
$icon_src = 'include/images/default-icon.png'; // Default icon
if (!empty($bookmark['icon'])) {
if (file_exists($bookmark['icon']) && strpos($bookmark['icon'], 'data/images/custom/') === 0) {
$icon_src = htmlspecialchars($bookmark['icon']);
} elseif (filter_var($bookmark['icon'], FILTER_VALIDATE_URL)) {
$icon_src = htmlspecialchars($bookmark['icon']);
} elseif (!empty($settings['favicon_extractor_url'])) {
$base_url = rtrim($settings['favicon_extractor_url'], '/');
$domain = htmlspecialchars($bookmark['icon']);
$icon_src = "{$base_url}/favicon/{$domain}?larger=true";
}
}
?>
<a href="<?php echo htmlspecialchars($bookmark['url']); ?>" <?php echo $target_attr; ?> class="list-group-item list-group-item-action d-flex align-items-center">
<img src="<?php echo $icon_src; ?>" alt="" class="bookmark-icon me-2" loading="lazy" onerror="this.onerror=null;this.src='include/images/default-icon.png';">
<span><?php echo htmlspecialchars($bookmark['title']); ?></span>
</a>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if (empty($favorites) && empty($categories)): ?>
<div class="text-center mt-5">
<h3><?php echo translate('index_welcome_title'); ?></h3>
<p class="lead"><?php echo translate('index_welcome_text'); ?></p>
<a href="bookmarks.php" class="btn btn-primary"><?php echo translate('index_welcome_button'); ?></a>
</div>
<?php endif; ?>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const tabsContainer = document.getElementById('tabs-container');
if (tabsContainer) {
new Sortable(tabsContainer, {
animation: 150,
onUpdate: function (evt) {
const tabIds = Array.from(tabsContainer.children).map(child => child.dataset.tabId);
// Send the new order to the server
fetch('ajax_update_tab_order.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ tab_ids: tabIds }),
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Optionally, show a success message
console.log('Tab order saved!');
} else {
// Handle error
console.error('Failed to save tab order.');
}
})
.catch((error) => {
console.error('Error:', error);
});
}
});
}
});
</script>
<?php require_once 'include/footer.php'; ?>