This commit introduces a new "Hard Reset" functionality in the settings, allowing users to reset the application to its initial state by deleting the configuration directory. Key changes include: - Added a "Hard Reset" button and a dedicated confirmation frame in `settings_frame.py`. - Implemented the logic to delete the `.config/py_backup` directory and restart the application. - Enhanced the hard reset process to unmount encrypted drives if they are mounted, prompting for a password if necessary. To improve modularity and maintainability, the PasswordDialog class has been refactored: - Moved PasswordDialog from `pyimage_ui/password_dialog.py` to `shared_libs/message.py`. - Updated all references and imports to the new location. - Externalized all user-facing strings in PasswordDialog for translation support. Additionally, several bug fixes and improvements were made: - Corrected object access hierarchy in `settings_frame.py` and `advanced_settings_frame.py` by passing manager instances directly. - Handled `FileNotFoundError` in `actions.py` when selecting remote backup destinations, preventing crashes and displaying "N/A" for disk usage. - Replaced incorrect `calculating_animation` reference with `animated_icon` in `actions.py`. - Added missing translation keys in `pbp_app_config.py`.
385 lines
17 KiB
Python
385 lines
17 KiB
Python
from pathlib import Path
|
|
from typing import Dict, Any
|
|
|
|
# Assuming the Translate class exists in shared_libs as per the template
|
|
from shared_libs.common_tools import Translate
|
|
|
|
|
|
class AppConfig:
|
|
"""Central configuration and system setup manager for PyImage Backup."""
|
|
|
|
# --- Core Paths ---
|
|
BASE_DIR: Path = Path.home()
|
|
APP_DIR: Path = BASE_DIR / ".config/py_backup"
|
|
SETTINGS_FILE: Path = APP_DIR / "pbp_settings.json"
|
|
GENERATED_EXCLUDE_LIST_PATH: Path = APP_DIR / "rsync-generated-excludes.conf"
|
|
USER_EXCLUDE_LIST_PATH: Path = APP_DIR / "user_excludes.txt"
|
|
MANUAL_EXCLUDE_LIST_PATH: Path = APP_DIR / "manual_excludes.txt"
|
|
LOG_FILE_PATH: Path = APP_DIR / "py-backup.log"
|
|
LOCK_FILE_PATH: Path = APP_DIR / "pybackup.lock"
|
|
APP_ICONS_DIR: Path = Path(__file__).parent / "lx-icons"
|
|
|
|
# --- Application Info ---
|
|
VERSION: str = "v.1.0.0"
|
|
UPDATE_URL: str = "" # To be defined later
|
|
|
|
# --- UI Configuration ---
|
|
UI_CONFIG: Dict[str, Any] = {
|
|
"window_title": "PyBackup",
|
|
"window_size": "1200x800",
|
|
"font_family": "Ubuntu",
|
|
"font_size": 11,
|
|
}
|
|
|
|
@staticmethod
|
|
def _get_user_dirs() -> Dict[str, Path]:
|
|
"""
|
|
Parses ~/.config/user-dirs.dirs to get localized user folder paths.
|
|
"""
|
|
user_dirs_path = Path.home() / ".config/user-dirs.dirs"
|
|
user_dirs = {}
|
|
if user_dirs_path.exists():
|
|
try:
|
|
with open(user_dirs_path, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line and not line.startswith('#') and '=' in line:
|
|
key, value = line.split('=', 1)
|
|
value = value.strip().strip('"')
|
|
if value.startswith("$HOME"):
|
|
value = value.replace(
|
|
"$HOME", str(Path.home()))
|
|
user_dirs[key] = Path(value)
|
|
except Exception:
|
|
# In case of any error, just return empty dict
|
|
return {}
|
|
return user_dirs
|
|
|
|
user_dirs = _get_user_dirs.__func__()
|
|
|
|
# --- Folder Paths ---
|
|
FOLDER_PATHS: Dict[str, Path] = {
|
|
"Computer": Path("/"),
|
|
"Documents": user_dirs.get("XDG_DOCUMENTS_DIR", Path.home() / "Documents"),
|
|
"Pictures": user_dirs.get("XDG_PICTURES_DIR", Path.home() / "Pictures"),
|
|
"Music": user_dirs.get("XDG_MUSIC_DIR", Path.home() / "Music"),
|
|
"Videos": user_dirs.get("XDG_VIDEOS_DIR", Path.home() / "Videos"),
|
|
}
|
|
|
|
STANDARD_EXCLUDE_CONTENT = """/dev/*
|
|
/proc/*
|
|
/sys/*
|
|
/media/*
|
|
/mnt/*
|
|
**/tmp/
|
|
/run/*
|
|
/var/run/*
|
|
/var/lock/*
|
|
/var/lib/docker/*
|
|
/var/lib/libvirt/images/*
|
|
/var/lib/schroot/*
|
|
/data/*
|
|
/DATA/*
|
|
/cdrom/*
|
|
/sdcard/*
|
|
/lost+found
|
|
/swapfile
|
|
/system/*
|
|
**/root/.gvfs
|
|
/snap/*
|
|
/home/*/.gvfs
|
|
/home/*/.cache/*
|
|
/var/cache/pacman/pkg/*
|
|
/var/cache/apt/archives/*
|
|
/var/cache/yum/*
|
|
/var/cache/dnf/*
|
|
/var/cache/eopkg/*
|
|
/var/cache/xbps/*
|
|
/var/cache/zypp/*
|
|
/var/cache/edb/*
|
|
/home/*/.local/share/gnome_boxes/
|
|
/home/*/leagueoflegends/
|
|
/home/*/.local/share/leagueoflegends/
|
|
/home/*/.config/lutris/
|
|
/home/*/.local/share/lutris/
|
|
/home/*/.minecraft
|
|
/home/*/.thunderbird
|
|
/home/*/.minetest
|
|
/home/*/.steam
|
|
/home/*/.local/share/Steam/
|
|
/home/*/.local/share/supertux2/
|
|
/home/*/.local/share/supertuxkart/
|
|
/home/*/.cache/supertuxkart/
|
|
/home/*/.config/supertuxkart/
|
|
/home/*/.local/share/applications/wine/
|
|
/home/*/.wine/
|
|
/home/*/rsynclog
|
|
/home/*/rsynclog.old
|
|
/home/*/.local/share/[Tt]rash
|
|
/home/*/.opera/cache
|
|
/home/*/.kde/share/apps/kio_http/cache
|
|
/home/*/.kde/share/cache/http
|
|
/home/*/.var/*
|
|
/home/*/.lmstudio"""
|
|
|
|
@classmethod
|
|
def ensure_directories(cls) -> None:
|
|
"""Ensures that all required application directories exist."""
|
|
if not cls.APP_DIR.exists():
|
|
cls.APP_DIR.mkdir(parents=True, exist_ok=True)
|
|
# In the future, we can create a default settings file here
|
|
|
|
# Generate/update the final exclude list on every start
|
|
cls.generate_and_write_final_exclude_list()
|
|
|
|
# Create default user excludes only if the file doesn't exist
|
|
if not cls.USER_EXCLUDE_LIST_PATH.exists():
|
|
cls.create_default_user_excludes()
|
|
|
|
@classmethod
|
|
def create_default_user_excludes(cls) -> None:
|
|
"""
|
|
Creates the user exclude file with default entries (visible home folders).
|
|
Overwrites the file if it exists.
|
|
"""
|
|
home_dir = Path.home()
|
|
try:
|
|
with open(cls.USER_EXCLUDE_LIST_PATH, 'w') as f:
|
|
for item in home_dir.iterdir():
|
|
if item.is_dir() and not item.name.startswith('.'):
|
|
f.write(f"{str(item.absolute())}/*\n")
|
|
except IOError as e:
|
|
# Using print for now, as logger might not be available here
|
|
print(f"Error creating default user exclude list: {e}")
|
|
|
|
@classmethod
|
|
def generate_and_write_final_exclude_list(cls) -> None:
|
|
"""
|
|
Generates and writes the final rsync exclude list (rsync-exclude.conf)
|
|
containing standard patterns.
|
|
"""
|
|
# Write standard excludes to generated list
|
|
try:
|
|
with open(cls.GENERATED_EXCLUDE_LIST_PATH, 'w') as f:
|
|
f.write(cls.STANDARD_EXCLUDE_CONTENT)
|
|
except IOError as e:
|
|
pass # Handle error in caller
|
|
|
|
|
|
# --- Translation Setup ---
|
|
# Initialize translations for the app domain "pyimage_backup"
|
|
_ = Translate.setup_translations("pyimage_backup")
|
|
|
|
|
|
class Msg:
|
|
"""Provides centralized access to all translated UI strings."""
|
|
|
|
TTIP: Dict[str, str] = {
|
|
"theme_toggle": "",
|
|
"tooltips_toggle": "",
|
|
"updates_toggle": "",
|
|
"show_log": "",
|
|
"about_app": "",
|
|
"updates_disabled": "",
|
|
"no_server_conn_tt": "",
|
|
"up_to_date": "",
|
|
"install_new_version": "",
|
|
}
|
|
STR: Dict[str, str] = {
|
|
# General
|
|
"app_title": _("Lx Tools Pybackup"),
|
|
"welcome_msg": _("Please select an action from the menu."),
|
|
"app_started": _("Application started successfully."),
|
|
"app_quit": _("Application is shutting down."),
|
|
"browse": _("Browse..."),
|
|
"start_backup": _("Start Backup"),
|
|
"restore": _("Restore"),
|
|
"error": _("Error"),
|
|
"confirm": _("Confirm"),
|
|
"warning": _("Warning"),
|
|
"cancel": _("Cancel"),
|
|
"yes": _("Yes"),
|
|
"no": _("No"),
|
|
"apply": _("Apply"),
|
|
"delete": _("Delete"),
|
|
"add": _("Add"),
|
|
"remove": _("Remove"),
|
|
"back": _("Back"),
|
|
"active": _("Active"),
|
|
"source": _("Source"),
|
|
"select_source": _("Select Source"),
|
|
"sources": _("Sources"),
|
|
"destination": _("Destination"),
|
|
"system_backup_info": _("System Backup"),
|
|
"system_restore_info": _("System Restore"),
|
|
"system_restore": _("System Restore"),
|
|
"select_source_first": _("Please select a source first"),
|
|
"user_backup_info": _("User Backup"),
|
|
"user_restore_info": _("User Restore"),
|
|
"select_destination_first": _("Please select a destination first"),
|
|
"calculating_size": _("Calculating size..."),
|
|
"select_destination": _("Select Destination"),
|
|
"settings_reset_title": _("Settings Reset"),
|
|
"settings_reset_text": _("Settings have been reset to default values."),
|
|
"system_backup_in_home_error": _("System backups cannot be saved in the /home directory."),
|
|
"path_not_found": _("Path not found: {path}"),
|
|
"warning_source_larger_than_partition": _("Note: Source size > partition size. Check exclusions in advanced settings if necessary."),
|
|
"warning_not_enough_space": _("WARNING: Not enough space for the backup.\nPlease free up space or choose another location."),
|
|
"warning_space_over_90_percent": _("WARNING: The storage space will be over 90% full. Backup at your own risk!"),
|
|
"ready_for_first_backup": _("Everything is ready for your first backup."),
|
|
"backup_mode_info": _("Backup Mode: You can start a backup here."),
|
|
"restore_mode_info": _("Restore Mode: You can start a restore here."),
|
|
"advanced_settings_title": _("Advanced Settings"),
|
|
"animation_settings_title": _("Animation Settings"),
|
|
"backup_animation_label": _("Backup/Restore Animation:"),
|
|
"calc_animation_label": _("Size Calculation Animation:"),
|
|
"advanced_settings_warning": _("WARNING: Changing these settings is recommended for experienced users only. Incorrect configurations can lead to an unreliable backup.\n\nThe backup destination is always excluded for security reasons and cannot be changed here."),
|
|
"exclude_system_folders": _("Exclude system folders"),
|
|
"in_backup": _("In Backup"),
|
|
"name": _("Name"),
|
|
"path": _("Path"),
|
|
"date": _("Date"),
|
|
"time": _("Time"),
|
|
"size": _("Size"),
|
|
"type": _("Type"),
|
|
"folder": _("Folder"),
|
|
"backup_content": _("Backup Content"),
|
|
"scheduled_jobs": _("Scheduled Jobs"),
|
|
"err_no_job_selected": _("No job selected."),
|
|
"user_defined_folder_settings": _("User-defined folder settings"),
|
|
"hidden_files_and_folders": _("Hidden files and folders"),
|
|
"advanced": _("Advanced"),
|
|
"default_settings": _("Default settings"),
|
|
"scheduling": _("Scheduling"),
|
|
"settings": _("Settings"),
|
|
"log": _("Log"),
|
|
"full_backup": _("Full backup"),
|
|
"incremental": _("Incremental"),
|
|
"incremental_backup": _("Incremental backup"), # New
|
|
"test_run": _("Test run"),
|
|
"start": _("Start"),
|
|
"cancel_backup": _("Cancel"),
|
|
"backup_cancelled_and_deleted_msg": _("Backup cancelled and partially completed backup deleted."),
|
|
"info_text_placeholder": _("Info text about the current view."),
|
|
"backup_finished_successfully": _("Backup finished successfully."),
|
|
"backup_finished_with_warnings": _("Backup finished with warnings. See log for details."),
|
|
"backup_failed": _("Backup failed. See log for details."),
|
|
"backup_cancelled_by_user": _("Backup was cancelled by the user."),
|
|
"accurate_size_cb_label": _("Accurate inkrem. size"),
|
|
"accurate_size_info_label": _("(Calculation may take longer)"),
|
|
"accurate_size_success": _("Accurate size calculated successfully."),
|
|
"accurate_size_failed": _("Failed to calculate size. See log for details."),
|
|
"please_wait": _("Please wait, calculating..."),
|
|
"accurate_calc_cancelled": _("Calculate size cancelled."),
|
|
"add_to_exclude_list": _("Add to exclude list"),
|
|
"exclude_dialog_text": _("Do you want to add a folder or a file?"),
|
|
"add_folder_button": _("Folder"),
|
|
"add_file_button": _("File"),
|
|
"system_excludes": _("System Excludes"),
|
|
"manual_excludes": _("Manual Excludes"),
|
|
"manual_excludes_info": _("Here, manually add files or folders to be excluded from the backup. Each entry should be on a new line."),
|
|
|
|
# Menus
|
|
"file_menu": _("File"),
|
|
"exit_menu": _("Exit"),
|
|
"backup_menu": _("Backup"),
|
|
"system_backup_menu": _("System Backup"),
|
|
"user_backup_menu": _("User Data Backup"),
|
|
"restore_menu": _("Restore"),
|
|
"browse_backups_menu": _("Browse Backups"),
|
|
"help_menu": _("Help"),
|
|
"info_menu": _("Info"),
|
|
|
|
# Backup Views
|
|
"source_folders": _("Folders to back up"),
|
|
"dest_folder": _("Destination Folder"),
|
|
"backup_type": _("Backup Type"),
|
|
"type_incremental": _("Incremental (Recommended, saves space)"),
|
|
"type_full": _("Full (Forces a new, complete backup)"),
|
|
"cat_images": _("Pictures"),
|
|
"cat_documents": _("Documents"),
|
|
"cat_music": _("Music"),
|
|
"cat_videos": _("Videos"),
|
|
"show_encrypted_backups": _("Show Encrypted Backups"),
|
|
"show_normal_backups": _("Show Normal Backups"),
|
|
|
|
# Browser View
|
|
"backup_location": _("Backup Location"),
|
|
"found_backups": _("Found Backups"),
|
|
"col_backup_name": _("Backup Name"),
|
|
"col_type": _("Type"),
|
|
"col_date": _("Date"),
|
|
"type_system": _("System"),
|
|
"type_user": _("User Data"),
|
|
|
|
# Dialogs & Messages
|
|
"select_dest_folder_title": _("Select destination folder for backup"),
|
|
"select_backup_location_title": _("Select backup location"),
|
|
"err_no_dest_folder": _("Please select a destination folder."),
|
|
"err_no_source_folder": _("Please select at least one source folder."),
|
|
"err_no_backup_selected": _("Please select a backup from the list."),
|
|
"err_unlock_failed": _("Failed to unlock the container. Please check the password and try again."),
|
|
"err_encrypted_not_mounted": _("Encrypted container is not unlocked. Please unlock it first from the header bar."),
|
|
"confirm_user_restore_title": _("Confirm User Data Restore"),
|
|
"confirm_user_restore_msg": _("Do you really want to restore the backup of '{backup_name}' to its original location? Any newer files may be overwritten."),
|
|
"confirm_delete_title": _("Confirm Deletion"),
|
|
"confirm_delete_text": _("Do you really want to delete the backup '{folder_name}'? This action cannot be undone."),
|
|
"final_warning_system_restore_title": _("FINAL WARNING"),
|
|
"final_warning_system_restore_msg": _("ATTENTION: You are about to restore the system. This process cannot be safely interrupted. All changes since the backup will be lost. \n\nThe computer will automatically restart upon completion. \n\nREALLY PROCEED?"),
|
|
"btn_continue": _("PROCEED"),
|
|
"deleting_backup_in_progress": _("Deletion in progress... Please wait."),
|
|
"select_restore_source_title": _("Select Restore Source"),
|
|
"select_restore_destination_title": _("Select Restore Destination"),
|
|
|
|
# Lock Screen
|
|
"lock_title": _("System Restore in Progress"),
|
|
"lock_msg1": _("Please do not interrupt this process."),
|
|
"lock_msg2": _("The computer will automatically restart upon completion."),
|
|
"add_job_title": _("Add New Job"),
|
|
"frequency": _("Frequency"),
|
|
"freq_daily": _("Daily"),
|
|
"freq_weekly": _("Weekly"),
|
|
"freq_monthly": _("Monthly"),
|
|
"save": _("Save"),
|
|
"projected_usage_label": _("Projected usage after backup"),
|
|
"header_title": _("Lx Tools Py-Backup"),
|
|
"header_subtitle": _("Simple GUI for rsync"),
|
|
"encrypted_backup_content": _("Encrypted Backups"),
|
|
"compressed": _("Compressed"),
|
|
"compression": _("Compression"), # New
|
|
"encrypted": _("Encrypted"),
|
|
"encryption": _("Encryption"), # New
|
|
"bypass_security": _("Bypass security"),
|
|
"refresh_log": _("Refresh log"),
|
|
"comment": _("Kommentar"),
|
|
"force_full_backup": _("Always force full backup"),
|
|
"force_incremental_backup": _("Always force incremental backup (except first)"),
|
|
"force_compression": _("Always compress backup"),
|
|
"force_encryption": _("Always encrypt backup"),
|
|
"use_trash_bin": _("Archive outdated files in a trash bin"),
|
|
"no_trash_bin": _("Permanently delete outdated files (true sync)"),
|
|
"trash_bin_explanation": _("This setting only applies to User Backups, not System Backups. It controls how files that are deleted from your source (e.g., your Documents folder) are handled in the destination."),
|
|
"sync_mode_pure_sync": _("Sync Mode: Mirror. Files deleted from the source will also be permanently deleted from the backup."),
|
|
"sync_mode_trash_bin": _("Sync Mode: Archive. Files deleted from the source will be moved to a trash folder in the backup."),
|
|
"sync_mode_no_delete": _("Sync Mode: Additive. Files deleted from the source will be kept in the backup."),
|
|
"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
|
|
"backup_options": _("Backup Options"), # New
|
|
"hard_reset": _("Hard reset"),
|
|
"hard_reset_warning": _("This will reset the application to its initial state, as if it were opened for the first time. This can be useful if you are experiencing problems with the app. Clicking 'Delete now' will delete the '.config/py_backup' folder in your home directory without an additional dialog. The application will then automatically restart."),
|
|
"delete_now": _("Delete now"),
|
|
"full_delete_config_settings": _("Full delete config settings"),
|
|
"password_required": _("Password Required"),
|
|
"enter_password_prompt": _("Please enter the password for the encrypted backup:"),
|
|
"confirm_password_prompt": _("Confirm password:"),
|
|
"save_to_keyring": _("Save password to system keyring"),
|
|
"password_empty_error": _("Password cannot be empty."),
|
|
"passwords_do_not_match_error": _("Passwords do not match."),
|
|
"ok": _("OK"),
|
|
"unlock_backup": _("Unlock Backup"),
|
|
}
|