206 lines
8.0 KiB
PHP
206 lines
8.0 KiB
PHP
<?php
|
|
ini_set('display_errors', 1);
|
|
ini_set('display_startup_errors', 1);
|
|
error_reporting(E_ALL);
|
|
|
|
// Set the working directory to the project root
|
|
chdir(__DIR__ . '/..');
|
|
|
|
// --- Custom Error Handling ---
|
|
register_shutdown_function(function () {
|
|
$error = error_get_last();
|
|
if ($error !== null && !headers_sent()) {
|
|
http_response_code(500);
|
|
header('Content-Type: application/json');
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => 'A fatal server error occurred.',
|
|
'error_details' => $error
|
|
]);
|
|
}
|
|
});
|
|
|
|
require_once 'include/init.php';
|
|
require_once 'include/auth.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
if (!isset($_SESSION['user_id'])) {
|
|
http_response_code(401);
|
|
echo json_encode(['success' => false, 'message' => 'You must be logged in.']);
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Recursively removes a directory and its contents.
|
|
* @param string $dir The directory to remove.
|
|
*/
|
|
function rrmdir($dir) {
|
|
if (is_dir($dir)) {
|
|
$objects = scandir($dir);
|
|
foreach ($objects as $object) {
|
|
if ($object != "." && $object != "..") {
|
|
is_dir($dir . DIRECTORY_SEPARATOR . $object) ? rrmdir($dir . DIRECTORY_SEPARATOR . $object) : unlink($dir . DIRECTORY_SEPARATOR . $object);
|
|
}
|
|
}
|
|
rmdir($dir);
|
|
}
|
|
}
|
|
|
|
// --- Main Logic ---
|
|
try {
|
|
if (!isset($_FILES['backup_file']) || $_FILES['backup_file']['error'] !== UPLOAD_ERR_OK) {
|
|
throw new Exception('File upload error.');
|
|
}
|
|
if (!isset($_POST['target_tab_id']) || empty($_POST['target_tab_id'])) {
|
|
throw new Exception('Target tab not selected.');
|
|
}
|
|
|
|
$target_tab_id = (int)$_POST['target_tab_id'];
|
|
$backup_file = $_FILES['backup_file']['tmp_name'];
|
|
|
|
$temp_dir = sys_get_temp_dir() . '/bookmarkly_import_' . uniqid();
|
|
if (!mkdir($temp_dir, 0777, true)) {
|
|
throw new Exception('Failed to create temporary directory.');
|
|
}
|
|
|
|
$zip = new ZipArchive;
|
|
if ($zip->open($backup_file) !== TRUE) {
|
|
rrmdir($temp_dir);
|
|
throw new Exception('Failed to open the backup file.');
|
|
}
|
|
|
|
$zip->extractTo($temp_dir);
|
|
$zip->close();
|
|
|
|
$backup_db_path = $temp_dir . '/database/bookmarkly.db';
|
|
$manifest_path = $temp_dir . '/manifest.json';
|
|
|
|
if (!file_exists($backup_db_path)) {
|
|
throw new Exception('Invalid backup file: bookmarkly.db not found.');
|
|
}
|
|
|
|
$backup_pdo = new PDO('sqlite:' . $backup_db_path);
|
|
$backup_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$backup_pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
|
|
|
// --- Begin Transaction ---
|
|
$pdo->beginTransaction();
|
|
try {
|
|
$is_tab_backup = false;
|
|
if (file_exists($manifest_path)) {
|
|
$manifest = json_decode(file_get_contents($manifest_path), true);
|
|
if (isset($manifest['type']) && $manifest['type'] === 'tab') {
|
|
$is_tab_backup = true;
|
|
}
|
|
}
|
|
|
|
// --- Import Logic ---
|
|
$old_to_new_category_id_map = [];
|
|
$imported_categories_count = 0;
|
|
$imported_bookmarks_count = 0;
|
|
|
|
// If it's a tab backup, also import tab settings and files
|
|
if ($is_tab_backup) {
|
|
$backup_tab = $backup_pdo->query("SELECT * FROM tabs LIMIT 1")->fetch();
|
|
if ($backup_tab) {
|
|
// Handle background image
|
|
$new_background_url = $backup_tab['background_image_url'];
|
|
if (!empty($new_background_url) && strpos($new_background_url, 'images/backgrounds/') === 0) {
|
|
$source_bg = $temp_dir . '/' . $new_background_url;
|
|
if (file_exists($source_bg)) {
|
|
$bg_filename = basename($new_background_url);
|
|
$dest_dir = 'data/images/backgrounds/';
|
|
if (!is_dir($dest_dir)) mkdir($dest_dir, 0755, true);
|
|
|
|
$unique_filename = 'tab' . $target_tab_id . '_' . time() . '_' . $bg_filename;
|
|
$dest_bg = $dest_dir . $unique_filename;
|
|
if (copy($source_bg, $dest_bg)) {
|
|
$new_background_url = $dest_bg . '?v=' . time();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update target tab with settings from backup
|
|
$update_tab_stmt = $pdo->prepare(
|
|
"UPDATE tabs SET theme = ?, theme_transparency = ?, background_image_url = ?, background_brightness = ?, background_saturation = ? WHERE id = ?"
|
|
);
|
|
$update_tab_stmt->execute([
|
|
$backup_tab['theme'],
|
|
$backup_tab['theme_transparency'],
|
|
$new_background_url,
|
|
$backup_tab['background_brightness'],
|
|
$backup_tab['background_saturation'],
|
|
$target_tab_id
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Import Categories
|
|
$categories = $backup_pdo->query("SELECT * FROM categories")->fetchAll();
|
|
$stmt_insert_category = $pdo->prepare("INSERT INTO categories (name, sort_order, tab_id) VALUES (?, ?, ?)");
|
|
foreach ($categories as $category) {
|
|
$stmt_insert_category->execute([$category['name'], $category['sort_order'], $target_tab_id]);
|
|
$old_to_new_category_id_map[$category['id']] = $pdo->lastInsertId();
|
|
$imported_categories_count++;
|
|
}
|
|
|
|
// Import Bookmarks
|
|
$bookmarks = $backup_pdo->query("SELECT * FROM bookmarks")->fetchAll();
|
|
$stmt_insert_bookmark = $pdo->prepare("INSERT INTO bookmarks (category_id, title, url, icon, is_favorite, sort_order, tab_id) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
foreach ($bookmarks as $bookmark) {
|
|
// Determine the new category ID, allowing for bookmarks without a category
|
|
$new_category_id = null;
|
|
if (!empty($bookmark['category_id']) && isset($old_to_new_category_id_map[$bookmark['category_id']])) {
|
|
$new_category_id = $old_to_new_category_id_map[$bookmark['category_id']];
|
|
}
|
|
|
|
// Handle icon
|
|
$new_icon_path = $bookmark['icon'];
|
|
// In tab backups, icon paths are relative to the zip root, e.g., 'images/custom/icon.png'
|
|
if ($is_tab_backup && !empty($new_icon_path) && strpos($new_icon_path, 'images/custom/') === 0) {
|
|
$source_icon = $temp_dir . '/' . $new_icon_path;
|
|
if (file_exists($source_icon)) {
|
|
$icon_filename = basename($new_icon_path);
|
|
$dest_dir = 'data/images/custom/'; // The correct destination for custom icons
|
|
if (!is_dir($dest_dir)) mkdir($dest_dir, 0755, true);
|
|
|
|
$unique_filename = 'imported_' . time() . '_' . $icon_filename;
|
|
$dest_icon = $dest_dir . $unique_filename;
|
|
if (copy($source_icon, $dest_icon)) {
|
|
$new_icon_path = $dest_icon;
|
|
}
|
|
}
|
|
}
|
|
|
|
$stmt_insert_bookmark->execute([
|
|
$new_category_id, $bookmark['title'], $bookmark['url'],
|
|
$new_icon_path, $bookmark['is_favorite'], $bookmark['sort_order'], $target_tab_id
|
|
]);
|
|
$imported_bookmarks_count++;
|
|
}
|
|
|
|
$pdo->commit();
|
|
$message = "Import successful. Added {$imported_categories_count} categories and {$imported_bookmarks_count} bookmarks.";
|
|
echo json_encode(['success' => true, 'message' => $message]);
|
|
|
|
} catch (Exception $e) {
|
|
$pdo->rollBack();
|
|
throw new Exception('Database error during import: ' . $e->getMessage());
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
|
|
|
} finally {
|
|
if (isset($temp_dir) && is_dir($temp_dir)) {
|
|
rrmdir($temp_dir);
|
|
}
|
|
// Close DB connections
|
|
$pdo = null;
|
|
if(isset($backup_pdo)) {
|
|
$backup_pdo = null;
|
|
}
|
|
}
|