676 lines
35 KiB
PHP
676 lines
35 KiB
PHP
<?php
|
|
$page_title = 'Manage Bookmarks - Bookmarkly';
|
|
require_once 'include/auth.php';
|
|
|
|
// --- Security Check: Only Admin and User roles can access this page ---
|
|
if (!isAdmin() && !isUser()) {
|
|
header('Location: index.php');
|
|
exit;
|
|
}
|
|
|
|
// Flash messages for feedback
|
|
$message = '';
|
|
$message_type = '';
|
|
if (isset($_SESSION['flash_message'])) {
|
|
$message = $_SESSION['flash_message']['text'];
|
|
$message_type = $_SESSION['flash_message']['type'];
|
|
unset($_SESSION['flash_message']);
|
|
}
|
|
|
|
// --- Helper function for icon uploads ---
|
|
function handle_icon_upload($bookmark_id = null, $title = '') {
|
|
if (isset($_FILES['icon_upload']) && $_FILES['icon_upload']['error'] === UPLOAD_ERR_OK) {
|
|
$upload_dir = 'data/images/custom/';
|
|
if (!is_dir($upload_dir)) {
|
|
mkdir($upload_dir, 0755, true);
|
|
}
|
|
|
|
$file_info = pathinfo($_FILES['icon_upload']['name']);
|
|
$extension = strtolower($file_info['extension']);
|
|
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'ico'];
|
|
|
|
if (in_array($extension, $allowed_extensions)) {
|
|
// Sanitize title for filename
|
|
$sanitized_title = trim($title);
|
|
if (empty($sanitized_title)) {
|
|
$sanitized_title = 'bookmark';
|
|
} else {
|
|
$sanitized_title = strtolower($sanitized_title);
|
|
$sanitized_title = str_replace(' ', '_', $sanitized_title);
|
|
// Remove characters that are not letters, numbers, underscores, or hyphens
|
|
$sanitized_title = preg_replace('/[^a-z0-9_-]/', '', $sanitized_title);
|
|
// Truncate to a reasonable length
|
|
$sanitized_title = substr($sanitized_title, 0, 50);
|
|
}
|
|
|
|
$filename = 'bookmark_' . ($bookmark_id ?? 'id') . '_' . $sanitized_title . '.' . $extension;
|
|
$destination = $upload_dir . $filename;
|
|
|
|
// Before moving, if we are editing, we should delete the old file.
|
|
if ($bookmark_id) {
|
|
global $pdo;
|
|
$stmt = $pdo->prepare("SELECT icon FROM bookmarks WHERE id = ?");
|
|
$stmt->execute([$bookmark_id]);
|
|
$old_icon = $stmt->fetchColumn();
|
|
// Check if old icon exists, is custom, and is not the same path as the new one
|
|
if ($old_icon && file_exists($old_icon) && strpos($old_icon, 'data/images/custom/') === 0 && $old_icon !== $destination) {
|
|
unlink($old_icon);
|
|
}
|
|
}
|
|
|
|
if (move_uploaded_file($_FILES['icon_upload']['tmp_name'], $destination)) {
|
|
return $destination; // Return the path to be saved in DB
|
|
}
|
|
}
|
|
}
|
|
return null; // No upload or failed upload
|
|
}
|
|
|
|
|
|
// Handle POST requests for CRUD operations
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
// Check edit permissions for the submitted tab_id for all actions
|
|
$submitted_tab_id = $_POST['tab_id'] ?? $_GET['tab_id'] ?? null;
|
|
if (isset($_POST['action']) && !canEditTab($submitted_tab_id)) {
|
|
$_SESSION['flash_message'] = ['text' => 'You do not have permission to perform this action.', 'type' => 'danger'];
|
|
header('Location: bookmarks.php?tab_id=' . $submitted_tab_id);
|
|
exit;
|
|
}
|
|
|
|
$action = $_POST['action'] ?? '';
|
|
|
|
// --- Category Actions ---
|
|
if ($action === 'add_category') {
|
|
$category_name = trim($_POST['category_name'] ?? '');
|
|
$tab_id = filter_input(INPUT_POST, 'tab_id', FILTER_VALIDATE_INT);
|
|
if (!empty($category_name) && $tab_id) {
|
|
$sort_stmt = $pdo->prepare("SELECT MAX(sort_order) as max_sort FROM categories WHERE tab_id = ?");
|
|
$sort_stmt->execute([$tab_id]);
|
|
$max_sort = $sort_stmt->fetchColumn() ?: 0;
|
|
|
|
$stmt = $pdo->prepare("INSERT INTO categories (name, sort_order, tab_id) VALUES (?, ?, ?)");
|
|
if ($stmt->execute([$category_name, $max_sort + 1, $tab_id])) {
|
|
$_SESSION['flash_message'] = ['text' => 'Category added successfully.', 'type' => 'success'];
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Error adding category.', 'type' => 'danger'];
|
|
}
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Category name and tab ID are required.', 'type' => 'warning'];
|
|
}
|
|
} elseif ($action === 'delete_category') {
|
|
$category_id = filter_input(INPUT_POST, 'category_id', FILTER_VALIDATE_INT);
|
|
if ($category_id) {
|
|
$stmt = $pdo->prepare("DELETE FROM categories WHERE id = ?");
|
|
if ($stmt->execute([$category_id])) {
|
|
$_SESSION['flash_message'] = ['text' => 'Category and its bookmarks have been deleted.', 'type' => 'success'];
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Error deleting category.', 'type' => 'danger'];
|
|
}
|
|
}
|
|
} elseif ($action === 'edit_category') {
|
|
$category_id = filter_input(INPUT_POST, 'category_id', FILTER_VALIDATE_INT);
|
|
$category_name = trim($_POST['category_name'] ?? '');
|
|
if ($category_id && !empty($category_name)) {
|
|
$stmt = $pdo->prepare("UPDATE categories SET name = ? WHERE id = ?");
|
|
if ($stmt->execute([$category_name, $category_id])) {
|
|
$_SESSION['flash_message'] = ['text' => 'Category renamed successfully.', 'type' => 'success'];
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Error renaming category.', 'type' => 'danger'];
|
|
}
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Invalid data for renaming category.', 'type' => 'warning'];
|
|
}
|
|
}
|
|
|
|
// --- Bookmark Actions ---
|
|
elseif ($action === 'add_bookmark') {
|
|
$title = trim($_POST['title'] ?? '');
|
|
$url = trim($_POST['url'] ?? '');
|
|
if (!empty($url) && !preg_match('/^https?:\/\//i', $url)) {
|
|
$url = 'https://' . $url;
|
|
}
|
|
$category_id = filter_input(INPUT_POST, 'category_id', FILTER_VALIDATE_INT);
|
|
$icon_url = trim($_POST['icon_url'] ?? '');
|
|
$is_favorite = isset($_POST['is_favorite']) ? 1 : 0;
|
|
|
|
$icon = $icon_url; // Default to URL
|
|
$new_icon_path = handle_icon_upload();
|
|
if ($new_icon_path) {
|
|
$icon = $new_icon_path;
|
|
}
|
|
|
|
if ($title && $url && $category_id) {
|
|
$sort_stmt = $pdo->prepare("SELECT MAX(sort_order) as max_sort FROM bookmarks WHERE category_id = ?");
|
|
$sort_stmt->execute([$category_id]);
|
|
$max_sort = $sort_stmt->fetchColumn() ?: 0;
|
|
|
|
$stmt = $pdo->prepare(
|
|
"INSERT INTO bookmarks (category_id, title, url, icon, is_favorite, sort_order)
|
|
VALUES (?, ?, ?, ?, ?, ?)"
|
|
);
|
|
if ($stmt->execute([$category_id, $title, $url, $icon, $is_favorite, $max_sort + 1])) {
|
|
$bookmark_id = $pdo->lastInsertId(); // Get the ID of the new bookmark
|
|
|
|
// Handle icon upload after getting the ID
|
|
$new_icon_path = handle_icon_upload($bookmark_id, $title);
|
|
if ($new_icon_path) {
|
|
// Update the bookmark with the new icon path
|
|
$update_stmt = $pdo->prepare("UPDATE bookmarks SET icon = ? WHERE id = ?");
|
|
$update_stmt->execute([$new_icon_path, $bookmark_id]);
|
|
}
|
|
|
|
$_SESSION['flash_message'] = ['text' => 'Bookmark added successfully.', 'type' => 'success'];
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Error adding bookmark.', 'type' => 'danger'];
|
|
}
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Title, URL, and category are required.', 'type' => 'warning'];
|
|
}
|
|
} elseif ($action === 'delete_bookmark') {
|
|
$bookmark_id = filter_input(INPUT_POST, 'bookmark_id', FILTER_VALIDATE_INT);
|
|
if ($bookmark_id) {
|
|
$stmt = $pdo->prepare("DELETE FROM bookmarks WHERE id = ?");
|
|
if ($stmt->execute([$bookmark_id])) {
|
|
$_SESSION['flash_message'] = ['text' => 'Bookmark deleted.', 'type' => 'success'];
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Error deleting bookmark.', 'type' => 'danger'];
|
|
}
|
|
}
|
|
}
|
|
elseif ($action === 'edit_bookmark') {
|
|
$bookmark_id = filter_input(INPUT_POST, 'bookmark_id', FILTER_VALIDATE_INT);
|
|
$title = trim($_POST['title'] ?? '');
|
|
$url = trim($_POST['url'] ?? '');
|
|
if (!empty($url) && !preg_match('/^https?:\/\//i', $url)) {
|
|
$url = 'https://' . $url;
|
|
}
|
|
$category_id = filter_input(INPUT_POST, 'category_id', FILTER_VALIDATE_INT);
|
|
$icon_url = trim($_POST['icon_url'] ?? '');
|
|
$is_favorite = isset($_POST['is_favorite']) ? 1 : 0;
|
|
$remove_icon = ($_POST['remove_icon'] ?? '0') === '1';
|
|
|
|
if ($bookmark_id && $title && $url && $category_id) {
|
|
// Fetch current icon to manage file deletion
|
|
$stmt = $pdo->prepare("SELECT icon FROM bookmarks WHERE id = ?");
|
|
$stmt->execute([$bookmark_id]);
|
|
$current_icon = $stmt->fetchColumn();
|
|
|
|
$icon = $current_icon; // Keep old icon by default
|
|
|
|
$new_icon_path = handle_icon_upload($bookmark_id, $title);
|
|
|
|
if ($new_icon_path) {
|
|
// 1. New upload takes highest precedence
|
|
$icon = $new_icon_path;
|
|
} else if ($remove_icon) {
|
|
// 2. Remove icon takes next precedence
|
|
if ($current_icon && file_exists($current_icon) && strpos($current_icon, 'data/images/custom/') === 0) {
|
|
unlink($current_icon);
|
|
}
|
|
// After removing, use whatever is in the URL field (might be empty)
|
|
$icon = $icon_url;
|
|
} else {
|
|
// 3. No upload, no removal. Handle URL input field.
|
|
// If the current icon is NOT a custom file, we can just save the icon_url.
|
|
if (strpos($current_icon ?? '', 'data/images/custom/') !== 0) {
|
|
$icon = $icon_url;
|
|
}
|
|
// If it IS a custom file, we should only change it if the user provided a new icon_url.
|
|
else if (!empty($icon_url)) {
|
|
// User is switching from custom to URL. Delete the old file.
|
|
if (file_exists($current_icon)) {
|
|
unlink($current_icon);
|
|
}
|
|
$icon = $icon_url;
|
|
}
|
|
// Implicit else: current icon is custom, icon_url is empty. Do nothing to keep the custom icon.
|
|
}
|
|
|
|
$stmt = $pdo->prepare(
|
|
"UPDATE bookmarks SET title = ?, url = ?, category_id = ?, icon = ?, is_favorite = ? WHERE id = ?"
|
|
);
|
|
if ($stmt->execute([$title, $url, $category_id, $icon, $is_favorite, $bookmark_id])) {
|
|
$_SESSION['flash_message'] = ['text' => 'Bookmark updated successfully.', 'type' => 'success'];
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Error updating bookmark.', 'type' => 'danger'];
|
|
}
|
|
} else {
|
|
$_SESSION['flash_message'] = ['text' => 'Invalid data for updating bookmark.', 'type' => 'warning'];
|
|
}
|
|
}
|
|
|
|
|
|
// Redirect to the same page to prevent form resubmission, preserving the tab
|
|
$tab_id = $_POST['tab_id'] ?? $_GET['tab_id'] ?? 1;
|
|
header('Location: bookmarks.php?tab_id=' . $tab_id);
|
|
exit;
|
|
}
|
|
|
|
|
|
// Fetch visible tabs for the user
|
|
$current_user_id = $_SESSION['user_id'] ?? null;
|
|
$current_user_role = $_SESSION['role'] ?? null;
|
|
|
|
$tabs_stmt_sql = "SELECT DISTINCT t.* FROM tabs t ";
|
|
$params = [];
|
|
|
|
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 tabs
|
|
$tabs_stmt_sql .= "WHERE t.user_id IS NULL";
|
|
}
|
|
|
|
$tabs_stmt_sql .= " ORDER BY t.id";
|
|
|
|
$tabs_stmt = $pdo->prepare($tabs_stmt_sql);
|
|
$tabs_stmt->execute($params);
|
|
$tabs = $tabs_stmt->fetchAll();
|
|
|
|
// Fetch all categories and their bookmarks for display for the current tab
|
|
$current_tab_id = $_GET['tab_id'] ?? ($tabs[0]['id'] ?? 1);
|
|
|
|
// Check if the current user can edit the currently viewed tab
|
|
$can_edit = canEditTab($current_tab_id);
|
|
|
|
$stmt = $pdo->prepare("
|
|
SELECT
|
|
c.id as category_id,
|
|
c.name as category_name,
|
|
c.sort_order as category_sort_order,
|
|
b.id as bookmark_id,
|
|
b.title as bookmark_title,
|
|
b.url as bookmark_url,
|
|
b.icon as bookmark_icon,
|
|
b.is_favorite as bookmark_is_favorite,
|
|
b.sort_order as bookmark_sort_order
|
|
FROM categories c
|
|
LEFT JOIN bookmarks b ON c.id = b.category_id
|
|
WHERE c.tab_id = :tab_id
|
|
ORDER BY c.sort_order, c.name, b.sort_order, b.title
|
|
");
|
|
$stmt->execute(['tab_id' => $current_tab_id]);
|
|
|
|
$results = $stmt->fetchAll();
|
|
|
|
$categories = [];
|
|
foreach ($results as $row) {
|
|
if (!isset($categories[$row['category_id']])) {
|
|
$categories[$row['category_id']] = [
|
|
'id' => $row['category_id'],
|
|
'name' => $row['category_name'],
|
|
'bookmarks' => []
|
|
];
|
|
}
|
|
if ($row['bookmark_id']) {
|
|
$categories[$row['category_id']]['bookmarks'][] = [
|
|
'id' => $row['bookmark_id'],
|
|
'title' => $row['bookmark_title'],
|
|
'url' => $row['bookmark_url'],
|
|
'icon' => $row['bookmark_icon'],
|
|
'is_favorite' => $row['bookmark_is_favorite']
|
|
];
|
|
}
|
|
}
|
|
|
|
require_once 'include/header.php';
|
|
?>
|
|
<div class="container mt-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h1><?php echo translate('bookmarks_manage_title'); ?></h1>
|
|
<div>
|
|
<?php if (isAdmin()): ?>
|
|
<a href="tabs_settings.php" class="btn btn-outline-secondary">
|
|
<i class="bi bi-folder-symlink"></i> Manage Tabs
|
|
</a>
|
|
<?php endif; ?>
|
|
<?php if ($can_edit): ?>
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addBookmarkModal">
|
|
<i class="bi bi-plus-lg"></i> <?php echo translate('bookmarks_add_bookmark_button'); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab Navigation -->
|
|
<?php if (count($tabs) > 1): ?>
|
|
<ul class="nav nav-tabs mb-3">
|
|
<?php foreach ($tabs as $tab): ?>
|
|
<li class="nav-item">
|
|
<a class="nav-link <?php if ($tab['id'] == $current_tab_id) echo 'active'; ?>" href="?tab_id=<?php echo $tab['id']; ?>">
|
|
<?php echo htmlspecialchars($tab['name'] === 'Default' ? translate('tab_default_name') : $tab['name']); ?>
|
|
</a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($message): ?>
|
|
<div class="alert alert-<?php echo $message_type; ?> alert-dismissible fade show" role="alert">
|
|
<?php echo htmlspecialchars($message); ?>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="row">
|
|
<!-- Categories Column -->
|
|
<div class="col-md-5">
|
|
<h3><?php echo translate('bookmarks_categories'); ?></h3>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<!-- Add Category Form -->
|
|
<?php if ($can_edit): ?>
|
|
<form action="bookmarks.php" method="post" class="mb-3">
|
|
<input type="hidden" name="action" value="add_category">
|
|
<input type="hidden" name="tab_id" value="<?php echo $current_tab_id; ?>">
|
|
<div class="input-group">
|
|
<input type="text" name="category_name" class="form-control" placeholder="<?php echo translate('bookmarks_new_category_placeholder'); ?>" required>
|
|
<button class="btn btn-primary" type="submit"><i class="bi bi-plus-lg"></i> <?php echo translate('bookmarks_add_category_button'); ?></button>
|
|
</div>
|
|
</form>
|
|
<?php endif; ?>
|
|
<!-- Category List -->
|
|
<ul class="list-group" id="category-list">
|
|
<?php foreach ($categories as $category): ?>
|
|
<li class="list-group-item d-flex justify-content-between align-items-center" data-id="<?php echo $category['id']; ?>">
|
|
<div>
|
|
<i class="bi bi-grip-vertical sort-handle" style="cursor: move;"></i> <!-- Handle for sorting -->
|
|
<span class="ms-2"><?php echo htmlspecialchars($category['name']); ?></span>
|
|
</div>
|
|
<div>
|
|
<span class="badge bg-secondary rounded-pill me-2"><?php echo count($category['bookmarks']); ?></span>
|
|
<?php if ($can_edit): ?>
|
|
<button class="btn btn-sm btn-outline-secondary" title="Edit Category"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#editCategoryModal"
|
|
data-bs-id="<?php echo $category['id']; ?>"
|
|
data-bs-name="<?php echo htmlspecialchars($category['name']); ?>">
|
|
<i class="bi bi-pencil"></i>
|
|
</button>
|
|
<form action="bookmarks.php" method="post" class="d-inline" onsubmit="return confirm('<?php echo translate('bookmarks_delete_category_confirm'); ?>');">
|
|
<input type="hidden" name="action" value="delete_category">
|
|
<input type="hidden" name="category_id" value="<?php echo $category['id']; ?>">
|
|
<input type="hidden" name="tab_id" value="<?php echo $current_tab_id; ?>">
|
|
<button type="submit" class="btn btn-sm btn-outline-danger" title="Delete Category"><i class="bi bi-trash"></i></button>
|
|
</form>
|
|
<?php endif; ?>
|
|
</div>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bookmarks Column -->
|
|
<div class="col-md-7">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h3><?php echo translate('bookmarks_bookmarks'); ?></h3>
|
|
</div>
|
|
|
|
<?php if (empty($categories)): ?>
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<p class="text-muted"><?php echo translate('bookmarks_no_category_text'); ?></p>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php foreach ($categories as $category): ?>
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<?php echo htmlspecialchars($category['name']); ?>
|
|
</div>
|
|
<div class="card-body">
|
|
<?php if (empty($category['bookmarks'])): ?>
|
|
<p class="text-muted"><?php echo translate('bookmarks_category_empty'); ?></p>
|
|
<?php else: ?>
|
|
<ul class="list-group list-group-flush bookmark-list" data-category-id="<?php echo $category['id']; ?>">
|
|
<?php foreach ($category['bookmarks'] as $bookmark): ?>
|
|
<?php
|
|
$icon_src = 'include/images/default-icon.png'; // Default icon
|
|
if (!empty($bookmark['icon'])) {
|
|
// Check if it's a custom uploaded icon
|
|
if (file_exists($bookmark['icon']) && strpos($bookmark['icon'], 'data/images/custom/') === 0) {
|
|
$icon_src = htmlspecialchars($bookmark['icon']); // Add timestamp to prevent caching
|
|
}
|
|
// Check if the icon stored is a full URL
|
|
elseif (filter_var($bookmark['icon'], FILTER_VALIDATE_URL)) {
|
|
$icon_src = htmlspecialchars($bookmark['icon']);
|
|
} elseif (!empty($settings['favicon_extractor_url'])) {
|
|
// It's a domain, build the URL from the base
|
|
$base_url = rtrim($settings['favicon_extractor_url'], '/');
|
|
$domain = htmlspecialchars($bookmark['icon']);
|
|
$icon_src = "{$base_url}/favicon/{$domain}?larger=true";
|
|
}
|
|
}
|
|
?>
|
|
<li class="list-group-item d-flex justify-content-between align-items-center" data-id="<?php echo $bookmark['id']; ?>">
|
|
<span>
|
|
<i class="bi bi-grip-vertical sort-handle" style="cursor: move;"></i>
|
|
<img src="<?php echo $icon_src; ?>" class="ms-2 me-2" style="width: 16px; height: 16px;" onerror="this.onerror=null;this.src='include/images/default-icon.png';">
|
|
<a href="<?php echo htmlspecialchars($bookmark['url']); ?>" <?php if($settings['open_new_tab'] ?? '1' === '1') echo 'target="_blank"'; ?>><?php echo htmlspecialchars($bookmark['title']); ?></a>
|
|
<?php if ($bookmark['is_favorite']): ?>
|
|
<i class="bi bi-star-fill text-warning" title="Favorite"></i>
|
|
<?php endif; ?>
|
|
</span>
|
|
<div>
|
|
<?php if ($can_edit): ?>
|
|
<button class="btn btn-sm btn-outline-secondary"
|
|
title="Edit Bookmark"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#editBookmarkModal"
|
|
data-bs-id="<?php echo $bookmark['id']; ?>"
|
|
data-bs-title="<?php echo htmlspecialchars($bookmark['title']); ?>"
|
|
data-bs-url="<?php echo htmlspecialchars($bookmark['url']); ?>"
|
|
data-bs-category="<?php echo $category['id']; ?>"
|
|
data-bs-icon="<?php echo htmlspecialchars($bookmark['icon']); ?>"
|
|
data-bs-icon-src="<?php echo $icon_src; ?>"
|
|
data-bs-favorite="<?php echo $bookmark['is_favorite']; ?>">
|
|
<i class="bi bi-pencil"></i>
|
|
</button>
|
|
<form action="bookmarks.php" method="post" class="d-inline" onsubmit="return confirm('<?php echo translate('bookmarks_delete_bookmark_confirm'); ?>');">
|
|
<input type="hidden" name="action" value="delete_bookmark">
|
|
<input type="hidden" name="bookmark_id" value="<?php echo $bookmark['id']; ?>">
|
|
<input type="hidden" name="tab_id" value="<?php echo $current_tab_id; ?>">
|
|
<button type="submit" class="btn btn-sm btn-outline-danger" title="Delete Bookmark"><i class="bi bi-trash"></i></button>
|
|
</form>
|
|
<?php endif; ?>
|
|
</div>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Bookmark Modal -->
|
|
<div class="modal fade" id="addBookmarkModal" tabindex="-1" aria-labelledby="addBookmarkModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="addBookmarkModalLabel"><?php echo translate('modal_add_bookmark_title'); ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form action="bookmarks.php" method="POST" enctype="multipart/form-data">
|
|
<div class="modal-body">
|
|
<input type="hidden" name="action" value="add_bookmark">
|
|
<input type="hidden" name="tab_id" value="<?php echo $current_tab_id; ?>">
|
|
|
|
<div class="mb-3">
|
|
<label for="bookmark-title" class="form-label"><?php echo translate('modal_title'); ?></label>
|
|
<input type="text" class="form-control" id="bookmark-title" name="title" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="bookmark-url" class="form-label"><?php echo translate('modal_url'); ?></label>
|
|
<input type="text" class="form-control" id="bookmark-url" name="url" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="bookmark-category" class="form-label"><?php echo translate('modal_category'); ?></label>
|
|
<select class="form-select" id="bookmark-category" name="category_id">
|
|
<option value="" selected><?php echo translate('modal_select_category'); ?></option>
|
|
<?php foreach ($categories as $cat): ?>
|
|
<option value="<?php echo htmlspecialchars($cat['id']); ?>"><?php echo htmlspecialchars($cat['name']); ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="bookmark-icon-url" class="form-label"><?php echo translate('modal_icon_url'); ?></label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="bookmark-icon-url" name="icon_url">
|
|
<button class="btn btn-outline-secondary" type="button" id="fetch-favicon-add" title="<?php echo translate('modal_fetch_favicon_button'); ?>">
|
|
<i class="bi bi-arrow-clockwise"></i>
|
|
</button>
|
|
</div>
|
|
<div class="form-text"><?php echo translate('modal_icon_url_help'); ?></div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="bookmark-icon-upload" class="form-label"><?php echo translate('modal_icon_upload'); ?></label>
|
|
<input class="form-control" type="file" id="bookmark-icon-upload" name="icon_upload" accept="image/*">
|
|
</div>
|
|
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" value="1" id="bookmark-favorite" name="is_favorite">
|
|
<label class="form-check-label" for="bookmark-favorite">
|
|
<?php echo translate('modal_mark_as_favorite'); ?>
|
|
</label>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo translate('modal_close_button'); ?></button>
|
|
<button type="submit" class="btn btn-primary"><?php echo translate('modal_save_bookmark_button'); ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Bookmark Modal -->
|
|
<div class="modal fade" id="editBookmarkModal" tabindex="-1" aria-labelledby="editBookmarkModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="editBookmarkModalLabel"><?php echo translate('modal_edit_bookmark_title'); ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form action="bookmarks.php" method="POST" enctype="multipart/form-data">
|
|
<div class="modal-body">
|
|
<input type="hidden" name="action" value="edit_bookmark">
|
|
<input type="hidden" name="bookmark_id" id="edit-bookmark-id">
|
|
<input type="hidden" name="tab_id" value="<?php echo $current_tab_id; ?>">
|
|
|
|
<div class="mb-3">
|
|
<label for="edit-bookmark-title" class="form-label"><?php echo translate('modal_title'); ?></label>
|
|
<input type="text" class="form-control" id="edit-bookmark-title" name="title" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="edit-bookmark-url" class="form-label"><?php echo translate('modal_url'); ?></label>
|
|
<input type="text" class="form-control" id="edit-bookmark-url" name="url" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="edit-bookmark-category" class="form-label"><?php echo translate('modal_category'); ?></label>
|
|
<select class="form-select" id="edit-bookmark-category" name="category_id" required>
|
|
<?php foreach ($categories as $category): ?>
|
|
<option value="<?php echo $category['id']; ?>"><?php echo htmlspecialchars($category['name']); ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="edit-bookmark-icon-url" class="form-label"><?php echo translate('modal_icon_url'); ?></label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="edit-bookmark-icon-url" name="icon_url">
|
|
<button class="btn btn-outline-secondary" type="button" id="fetch-favicon-edit" title="<?php echo translate('modal_fetch_favicon_button'); ?>">
|
|
<i class="bi bi-arrow-clockwise"></i>
|
|
</button>
|
|
</div>
|
|
<div class="form-text"><?php echo translate('modal_icon_url_help'); ?></div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="edit-bookmark-icon-upload" class="form-label"><?php echo translate('modal_icon_upload'); ?></label>
|
|
<input class="form-control" type="file" id="edit-bookmark-icon-upload" name="icon_upload" accept="image/*">
|
|
</div>
|
|
|
|
<div class="mb-3" id="current-icon-container">
|
|
<label class="form-label"><?php echo translate('modal_current_icon'); ?></label>
|
|
<div>
|
|
<img id="current-icon-preview" src="" alt="Current Icon" style="max-width: 32px; max-height: 32px; margin-right: 10px;">
|
|
<button type="button" id="remove-custom-icon" class="btn btn-sm btn-danger"><?php echo translate('modal_remove_icon_button'); ?></button>
|
|
<input type="hidden" name="remove_icon" id="remove-icon-input" value="0">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" value="1" id="edit-bookmark-favorite" name="is_favorite">
|
|
<label class="form-check-label" for="edit-bookmark-favorite">
|
|
<?php echo translate('modal_mark_as_favorite'); ?>
|
|
</label>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo translate('modal_close_button'); ?></button>
|
|
<button type="submit" class="btn btn-primary"><?php echo translate('modal_save_changes_button'); ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Category Modal -->
|
|
<div class="modal fade" id="editCategoryModal" tabindex="-1" aria-labelledby="editCategoryModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="editCategoryModalLabel"><?php echo translate('modal_edit_category_title'); ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form action="bookmarks.php" method="POST">
|
|
<div class="modal-body">
|
|
<input type="hidden" name="action" value="edit_category">
|
|
<input type="hidden" name="category_id" id="edit-category-id">
|
|
<input type="hidden" name="tab_id" value="<?php echo $current_tab_id; ?>">
|
|
<div class="mb-3">
|
|
<label for="edit-category-name" class="form-label"><?php echo translate('modal_category_name'); ?></label>
|
|
<input type="text" class="form-control" id="edit-category-name" name="category_name" required>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo translate('modal_close_button'); ?></button>
|
|
<button type="submit" class="btn btn-primary"><?php echo translate('modal_save_changes_button'); ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php require_once 'include/footer.php'; ?>
|