269 lines
12 KiB
PHP
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'; ?>
|