advance settings edit part one
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user