feat: Implementierung von Papierkorb- und reinen Synchronisierungsoptionen für Benutzer-Backups

This commit is contained in:
2025-09-06 17:33:22 +02:00
parent a843a875c6
commit 069d2ea94d
4 changed files with 52 additions and 5 deletions

View File

@@ -82,7 +82,7 @@ class BackupManager:
finally:
self.inhibit_cookie = None
def start_backup(self, queue, source_path: str, dest_path: str, is_system: bool, is_dry_run: bool = False, exclude_files: Optional[List[Path]] = None, source_size: int = 0, is_compressed: bool = False, is_encrypted: bool = False, mode: str = "incremental"):
def start_backup(self, queue, source_path: str, dest_path: str, is_system: bool, is_dry_run: bool = False, exclude_files: Optional[List[Path]] = None, source_size: int = 0, is_compressed: bool = False, is_encrypted: bool = False, mode: str = "incremental", use_trash_bin: bool = False, no_trash_bin: bool = False):
self.is_system_process = is_system
self._inhibit_screensaver()
@@ -95,12 +95,12 @@ class BackupManager:
return None
thread = threading.Thread(target=self._run_backup_path, args=(
queue, source_path, dest_path, is_system, is_dry_run, exclude_files, source_size, is_compressed, is_encrypted, mode, mount_point))
queue, source_path, dest_path, is_system, is_dry_run, exclude_files, source_size, is_compressed, is_encrypted, mode, mount_point, use_trash_bin, no_trash_bin))
thread.daemon = True
thread.start()
return thread
def _run_backup_path(self, queue, source_path: str, dest_path: str, is_system: bool, is_dry_run: bool, exclude_files: Optional[List[Path]], source_size: int, is_compressed: bool, is_encrypted: bool, mode: str, mount_point: Optional[str]):
def _run_backup_path(self, queue, source_path: str, dest_path: str, is_system: bool, is_dry_run: bool, exclude_files: Optional[List[Path]], source_size: int, is_compressed: bool, is_encrypted: bool, mode: str, mount_point: Optional[str], use_trash_bin: bool, no_trash_bin: bool):
try:
base_dest_path = os.path.dirname(dest_path)
pybackup_dir = os.path.join(base_dest_path, "pybackup")
@@ -140,6 +140,19 @@ class BackupManager:
command.append(f"--exclude-from={AppConfig.MANUAL_EXCLUDE_LIST_PATH}")
if is_dry_run:
command.append('--dry-run')
# Handle trash bin / pure sync options for user backups
if not is_system:
trash_bin_path = os.path.join(rsync_base_dest, ".Trash")
if use_trash_bin:
command.extend(['--backup', f'--backup-dir={trash_bin_path}', '--delete'])
# Exclude the trash bin itself from the backup
command.append(f"--exclude={os.path.basename(trash_bin_path)}/")
elif no_trash_bin:
command.append('--delete')
# Exclude the trash bin itself from the backup if it exists from previous use_trash_bin
command.append(f"--exclude={os.path.basename(trash_bin_path)}/")
command.extend([source_path, rsync_dest])
self.logger.log(f"Rsync command: {' '.join(command)}")

View File

@@ -352,5 +352,10 @@ class Msg:
"force_incremental_backup": _("Always force incremental backup (except first)"),
"force_compression": _("Always compress backup"),
"force_encryption": _("Always encrypt backup"),
"use_trash_bin": _("Papierkorb verwenden"),
"no_trash_bin": _("Kein Papierkorb (reine Synchronisierung)"),
"sync_mode_pure_sync": _("Synchronisierungsmodus: Reine Synchronisierung (Dateien werden gelöscht)"),
"sync_mode_trash_bin": _("Synchronisierungsmodus: Papierkorb verwenden (Gelöschte Dateien werden verschoben)"),
"sync_mode_no_delete": _("Synchronisierungsmodus: Keine Löschung (Dateien bleiben im Ziel)"),
"encryption_note_system_backup": _("Note: For system backups, encryption only applies to files directly within the /home directory. Folders are not automatically encrypted unless explicitly included in the backup."),
}

View File

@@ -384,6 +384,7 @@ class MainApplication(tk.Tk):
self.after(100, self.actions.on_sidebar_button_click,
restore_dest_folder)
self._process_queue()
self._update_sync_mode_display() # Call after loading state
def _setup_log_window(self):
self.log_frame = ttk.Frame(self.content_frame)
@@ -427,6 +428,10 @@ class MainApplication(tk.Tk):
self.info_checkbox_frame, text=Msg.STR["info_text_placeholder"])
self.info_label.pack(anchor=tk.W, fill=tk.X, pady=5)
self.sync_mode_label = ttk.Label(
self.info_checkbox_frame, text="", foreground="blue")
self.sync_mode_label.pack(anchor=tk.W, fill=tk.X, pady=2)
self.time_info_frame = ttk.Frame(self.info_checkbox_frame)
self.time_info_frame.pack(anchor=tk.W, fill=tk.X, pady=5)
@@ -748,6 +753,22 @@ class MainApplication(tk.Tk):
self.encrypted_cb.config(state="disabled")
self.actions._refresh_backup_options_ui()
self._update_sync_mode_display() # Update sync mode display after options are loaded
def _update_sync_mode_display(self):
use_trash_bin = self.config_manager.get_setting("use_trash_bin", False)
no_trash_bin = self.config_manager.get_setting("no_trash_bin", False)
if self.left_canvas_data.get('folder') == "Computer":
self.sync_mode_label.config(text="") # Not applicable for system backups
return
if no_trash_bin:
self.sync_mode_label.config(text=Msg.STR["sync_mode_pure_sync"], foreground="red")
elif use_trash_bin:
self.sync_mode_label.config(text=Msg.STR["sync_mode_trash_bin"], foreground="orange")
else:
self.sync_mode_label.config(text=Msg.STR["sync_mode_no_delete"], foreground="green")
if __name__ == "__main__":

View File

@@ -245,6 +245,7 @@ class Actions:
self._start_left_canvas_calculation(
button_text, str(folder_path), icon_name, extra_info)
self.app._update_sync_mode_display() # Update sync mode display when source changes
def _start_left_canvas_calculation(self, button_text, folder_path, icon_name, extra_info):
self.app.start_pause_button.config(state="disabled")
@@ -723,6 +724,11 @@ class Actions:
is_dry_run = self.app.testlauf_var.get()
is_compressed = self.app.compressed_var.get()
use_trash_bin = self.app.config_manager.get_setting("use_trash_bin", False)
no_trash_bin = self.app.config_manager.get_setting("no_trash_bin", False)
# Determine mode for user backup based on UI selection
mode = "full" if self.app.vollbackup_var.get() else "incremental"
self.app.backup_manager.start_backup(
queue=self.app.queue,
@@ -734,5 +740,7 @@ class Actions:
source_size=source_size_bytes,
is_compressed=is_compressed,
is_encrypted=is_encrypted,
mode='full',
password=password)
mode=mode,
password=password,
use_trash_bin=use_trash_bin,
no_trash_bin=no_trash_bin)