advance settings edit part one

This commit is contained in:
2025-09-06 18:14:41 +02:00
parent 069d2ea94d
commit 2097573cbc
2 changed files with 103 additions and 49 deletions

View File

@@ -358,4 +358,9 @@ class Msg:
"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."),
"keyfile_settings": _("Keyfile Settings"), # New
"backup_defaults_title": _("Backup Defaults"), # New
"automation_settings_title": _("Automation Settings"), # New
"create_add_key_file": _("Create/Add Key File"), # New
"key_file_not_created": _("Key file not created."), # New
}

View File

@@ -9,6 +9,7 @@ from pyimage_ui.shared_logic import enforce_backup_type_exclusivity
from shared_libs.message import MessageDialog
from pyimage_ui.password_dialog import PasswordDialog
class AdvancedSettingsFrame(tk.Toplevel):
def __init__(self, master, config_manager, app_instance, **kwargs):
super().__init__(master, **kwargs)
@@ -31,6 +32,12 @@ class AdvancedSettingsFrame(tk.Toplevel):
self.nav_buttons_defs = [
(Msg.STR["system_excludes"], lambda: self._switch_view(0)),
(Msg.STR["manual_excludes"], lambda: self._switch_view(1)),
# New button for Keyfile/Automation
(Msg.STR["keyfile_settings"], lambda: self._switch_view(2)),
(Msg.STR["animation_settings_title"],
lambda: self._switch_view(3)), # Animation settings
(Msg.STR["backup_defaults_title"],
lambda: self._switch_view(4)), # Backup Defaults
]
self.nav_buttons = []
@@ -83,75 +90,80 @@ class AdvancedSettingsFrame(tk.Toplevel):
self.manual_excludes_frame, text=Msg.STR["delete"], command=self._delete_manual_exclude)
delete_button.pack(pady=5)
animation_frame = ttk.LabelFrame(
self, text=Msg.STR["animation_settings_title"], padding=10)
animation_frame.pack(fill=tk.X, padx=10, pady=5)
self.animation_settings_frame = ttk.LabelFrame(
view_container, text=Msg.STR["animation_settings_title"], padding=10)
animation_types = ["counter_arc", "double_arc", "line", "blink"]
ttk.Label(animation_frame, text=Msg.STR["backup_animation_label"]).grid(
ttk.Label(self.animation_settings_frame, text=Msg.STR["backup_animation_label"]).grid(
row=0, column=0, sticky="w", pady=2)
self.backup_anim_var = tk.StringVar()
self.backup_anim_combo = ttk.Combobox(
animation_frame, textvariable=self.backup_anim_var, values=animation_types, state="readonly")
self.animation_settings_frame, textvariable=self.backup_anim_var, values=animation_types, state="readonly")
self.backup_anim_combo.grid(row=0, column=1, sticky="ew", padx=5)
ttk.Label(animation_frame, text=Msg.STR["calc_animation_label"]).grid(
ttk.Label(self.animation_settings_frame, text=Msg.STR["calc_animation_label"]).grid(
row=1, column=0, sticky="w", pady=2)
self.calc_anim_var = tk.StringVar()
self.calc_anim_combo = ttk.Combobox(
animation_frame, textvariable=self.calc_anim_var, values=animation_types, state="readonly")
self.animation_settings_frame, textvariable=self.calc_anim_var, values=animation_types, state="readonly")
self.calc_anim_combo.grid(row=1, column=1, sticky="ew", padx=5)
reset_button = ttk.Button(
animation_frame, text=Msg.STR["default_settings"], command=self._reset_animation_settings)
self.animation_settings_frame, text=Msg.STR["default_settings"], command=self._reset_animation_settings)
reset_button.grid(row=0, column=2, rowspan=2, padx=10)
animation_frame.columnconfigure(1, weight=1)
self.animation_settings_frame.columnconfigure(1, weight=1)
defaults_frame = ttk.LabelFrame(
self, text="Backup Defaults", padding=10)
defaults_frame.pack(fill=tk.X, padx=10, pady=5)
self.backup_defaults_frame = ttk.LabelFrame(
view_container, text=Msg.STR["backup_defaults_title"], padding=10)
self.force_full_var = tk.BooleanVar()
self.force_incremental_var = tk.BooleanVar()
self.force_compression_var = tk.BooleanVar()
self.force_encryption_var = tk.BooleanVar()
ttk.Checkbutton(defaults_frame, text=Msg.STR["force_full_backup"], variable=self.force_full_var, command=lambda: enforce_backup_type_exclusivity(
ttk.Checkbutton(self.backup_defaults_frame, text=Msg.STR["force_full_backup"], variable=self.force_full_var, command=lambda: enforce_backup_type_exclusivity(
self.force_full_var, self.force_incremental_var, self.force_full_var.get())).pack(anchor=tk.W)
ttk.Checkbutton(defaults_frame, text=Msg.STR["force_incremental_backup"], variable=self.force_incremental_var, command=lambda: enforce_backup_type_exclusivity(
ttk.Checkbutton(self.backup_defaults_frame, text=Msg.STR["force_incremental_backup"], variable=self.force_incremental_var, command=lambda: enforce_backup_type_exclusivity(
self.force_incremental_var, self.force_full_var, self.force_incremental_var.get())).pack(anchor=tk.W)
ttk.Checkbutton(defaults_frame, text=Msg.STR["force_compression"],
ttk.Checkbutton(self.backup_defaults_frame, text=Msg.STR["force_compression"],
variable=self.force_compression_var).pack(anchor=tk.W)
ttk.Checkbutton(defaults_frame, text=Msg.STR["force_encryption"],
ttk.Checkbutton(self.backup_defaults_frame, text=Msg.STR["force_encryption"],
variable=self.force_encryption_var).pack(anchor=tk.W)
ttk.Separator(defaults_frame, orient=tk.HORIZONTAL).pack(
ttk.Separator(self.backup_defaults_frame, orient=tk.HORIZONTAL).pack(
fill=tk.X, pady=5)
encryption_note = ttk.Label(
defaults_frame, text=Msg.STR["encryption_note_system_backup"], wraplength=750, justify="left")
self.backup_defaults_frame, text=Msg.STR["encryption_note_system_backup"], wraplength=750, justify="left")
encryption_note.pack(anchor=tk.W, pady=5)
# --- Automation Settings ---
automation_frame = ttk.LabelFrame(self, text="Automation (Cronjob)", padding=10)
automation_frame.pack(fill=tk.X, padx=10, pady=5)
# --- Keyfile/Automation Settings ---
self.keyfile_settings_frame = ttk.LabelFrame(
view_container, text=Msg.STR["automation_settings_title"], padding=10)
key_file_button = ttk.Button(automation_frame, text="Create/Add Key File", command=self._create_key_file)
key_file_button = ttk.Button(
self.keyfile_settings_frame, text=Msg.STR["create_add_key_file"], command=self._create_key_file)
key_file_button.grid(row=0, column=0, padx=5, pady=5)
self.key_file_status_var = tk.StringVar(value="Key file not created.")
key_file_status_label = ttk.Label(automation_frame, textvariable=self.key_file_status_var, foreground="gray")
self.key_file_status_var = tk.StringVar(
value=Msg.STR["key_file_not_created"])
key_file_status_label = ttk.Label(
self.keyfile_settings_frame, textvariable=self.key_file_status_var, foreground="gray")
key_file_status_label.grid(row=0, column=1, padx=5, pady=5, sticky="w")
sudoers_info_text = (f"To run automated backups, an administrator must create a file in /etc/sudoers.d/\n"
f"with the following content (replace 'punix' with the correct username):\n"
f"punix ALL=(ALL) NOPASSWD: /path/to/pybackup-cli.py") # Path needs to be updated
sudoers_info_label = ttk.Label(automation_frame, text=sudoers_info_text, justify="left")
sudoers_info_label.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5)
sudoers_info_text = (f"To run automated backups, an administrator must create a file in /etc/sudoers.d/\n"
f"with the following content (replace 'punix' with the correct username):\n"
# Path needs to be updated
f"punix ALL=(ALL) NOPASSWD: /path/to/pybackup-cli.py")
sudoers_info_label = ttk.Label(
self.keyfile_settings_frame, text=sudoers_info_text, justify="left")
automation_frame.columnconfigure(1, weight=1)
sudoers_info_label.grid(
row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5)
self.keyfile_settings_frame.columnconfigure(1, weight=1)
# --- Action Buttons ---
button_frame = ttk.Frame(self)
@@ -162,6 +174,18 @@ class AdvancedSettingsFrame(tk.Toplevel):
ttk.Button(button_frame, text=Msg.STR["cancel"], command=self.destroy).pack(
side=tk.LEFT, padx=5)
# Initial packing of frames (all hidden except the first one by _switch_view)
# Initially packed, then hidden by _switch_view
self.tree_frame.pack(fill=tk.BOTH, expand=True)
# Initially packed, then hidden by _switch_view
self.manual_excludes_frame.pack(fill=tk.BOTH, expand=True)
# Initially packed, then hidden by _switch_view
self.keyfile_settings_frame.pack(fill=tk.BOTH, expand=True)
# Initially packed, then hidden by _switch_view
self.animation_settings_frame.pack(fill=tk.BOTH, expand=True)
# Initially packed, then hidden by _switch_view
self.backup_defaults_frame.pack(fill=tk.BOTH, expand=True)
self._load_system_folders()
self._load_animation_settings()
self._load_backup_defaults()
@@ -172,55 +196,80 @@ class AdvancedSettingsFrame(tk.Toplevel):
def _create_key_file(self):
if not self.app_instance.destination_path:
MessageDialog(self, message_type="error", title="Error", text="Please select a backup destination first.")
MessageDialog(self, message_type="error", title="Error",
text="Please select a backup destination first.")
return
pybackup_dir = os.path.join(self.app_instance.destination_path, "pybackup")
pybackup_dir = os.path.join(
self.app_instance.destination_path, "pybackup")
container_path = os.path.join(pybackup_dir, "pybackup_encrypted.luks")
if not os.path.exists(container_path):
MessageDialog(self, message_type="error", title="Error", text="No encrypted container found at the destination.")
MessageDialog(self, message_type="error", title="Error",
text="No encrypted container found at the destination.")
return
# Prompt for the existing password to authorize adding a new key
password_dialog = PasswordDialog(self, title="Enter Existing Password", confirm=False)
password_dialog = PasswordDialog(
self, title="Enter Existing Password", confirm=False)
password, _ = password_dialog.get_password()
if not password:
return # User cancelled
return # User cancelled
key_file_path = self.app_instance.backup_manager.encryption_manager.create_and_add_key_file(self.app_instance.destination_path, password)
key_file_path = self.app_instance.backup_manager.encryption_manager.create_and_add_key_file(
self.app_instance.destination_path, password)
if key_file_path:
MessageDialog(self, message_type="info", title="Success", text=f"Key file created and added successfully!\nPath: {key_file_path}")
MessageDialog(self, message_type="info", title="Success",
text=f"Key file created and added successfully!\nPath: {key_file_path}")
else:
MessageDialog(self, message_type="error", title="Error", text="Failed to create or add key file. See log for details.")
MessageDialog(self, message_type="error", title="Error",
text="Failed to create or add key file. See log for details.")
self._update_key_file_status()
def _update_key_file_status(self):
if not self.app_instance.destination_path:
self.key_file_status_var.set("Key file status unknown (no destination set).")
self.key_file_status_var.set(
"Key file status unknown (no destination set).")
return
key_file_path = self.app_instance.backup_manager.encryption_manager.get_key_file_path(self.app_instance.destination_path)
key_file_path = self.app_instance.backup_manager.encryption_manager.get_key_file_path(
self.app_instance.destination_path)
if os.path.exists(key_file_path):
self.key_file_status_var.set(f"Key file exists: {key_file_path}")
else:
self.key_file_status_var.set("Key file has not been created for this destination.")
self.key_file_status_var.set(
"Key file has not been created for this destination.")
def _switch_view(self, index):
self.current_view_index = index
self.update_nav_buttons(index)
# Hide all frames first
self.tree_frame.pack_forget()
self.manual_excludes_frame.pack_forget()
self.keyfile_settings_frame.pack_forget()
self.animation_settings_frame.pack_forget()
self.backup_defaults_frame.pack_forget()
# Show the selected frame and update info label
if index == 0:
self.manual_excludes_frame.pack_forget()
self.tree_frame.pack(fill=tk.BOTH, expand=True)
self.info_label.config(
text=Msg.STR["advanced_settings_warning"])
else:
self.tree_frame.pack_forget()
self.info_label.config(text=Msg.STR["advanced_settings_warning"])
elif index == 1:
self.manual_excludes_frame.pack(fill=tk.BOTH, expand=True)
self.info_label.config(text=Msg.STR["manual_excludes_info"])
elif index == 2:
self.keyfile_settings_frame.pack(fill=tk.BOTH, expand=True)
# Use automation title for info
self.info_label.config(text=Msg.STR["automation_settings_title"])
elif index == 3:
self.animation_settings_frame.pack(fill=tk.BOTH, expand=True)
self.info_label.config(text=Msg.STR["animation_settings_title"])
elif index == 4:
self.backup_defaults_frame.pack(fill=tk.BOTH, expand=True)
self.info_label.config(text=Msg.STR["backup_defaults_title"])
def update_nav_buttons(self, active_index):
for i, button in enumerate(self.nav_buttons):
@@ -449,4 +498,4 @@ class AdvancedSettingsFrame(tk.Toplevel):
user_patterns.extend(
[line.strip() for line in f if line.strip() and not line.startswith('#')])
return generated_patterns, user_patterns
return generated_patterns, user_patterns