From 6bb3db5cf22ddab428a9f3eb4b9d55760ac189ff Mon Sep 17 00:00:00 2001 From: ehlinazgumus Date: Tue, 28 Jul 2026 16:57:02 +0300 Subject: [PATCH 1/2] fix: do not delete the panel configuration before checking the defaults exist restore_default_settings() removed the user's panel configuration and then copied the packaged defaults over it: rm -rf ~/.config/xfce4/panel/* ~/.config/.../xfce4-panel.xml; cp -R /etc/xdg/pardus/xfce4/panel/* ~/.config/xfce4/panel/; Two problems. The steps are joined with ';', so each one runs whether or not the previous succeeded, and subprocess.run() was called without check=True, so a failure was never reported. And the source it copies from belongs to pardus-xfce-settings, which is not a dependency of this package or of pardus-xfce-tweaks: pardus-xfce-tweaks Depends: python3-gi, python3:any, python3 (>= 3.5), gir1.2-gtk-3.0, gir1.2-xfconf-0, pardus-lib-xfce (>= 0.3) pardus-lib-xfce Depends: python3 (>= 3.5), python3-gi, gir1.2-glib-2.0, gir1.2-xfconf-0 On a system where that package is absent, /etc/xdg/pardus does not exist at all, so pressing "Restore Defaults" deleted the panel configuration, both cp calls failed silently, and the panel came back with nothing to load. The user lost their layout with no way to get it back and no error shown. Check for the defaults first and raise FileNotFoundError before touching anything the user owns, keep a .bak copy of the configuration since the operation is otherwise irreversible, and chain the shell steps with && plus check=True so a failure stops the sequence and reaches the caller. Callers should catch FileNotFoundError and tell the user the defaults are missing rather than failing silently. Declaring a dependency on pardus-xfce-settings would be worth doing as well; this change makes the missing-file case safe either way. --- src/PanelManager.py | 59 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/PanelManager.py b/src/PanelManager.py index 326211f..f97769e 100644 --- a/src/PanelManager.py +++ b/src/PanelManager.py @@ -1,3 +1,5 @@ +import os +import shutil import subprocess import gi @@ -89,14 +91,59 @@ def set_startup_icon(path): xfce4_panel.set_string(f"{whisker_plugin}/button-icon", path) +DEFAULT_PANEL_DIR = "/etc/xdg/pardus/xfce4/panel" +DEFAULT_PANEL_XML = ( + "/etc/xdg/pardus/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml" +) + + +def defaults_available(): + """Whether the packaged default panel configuration is present. + + These files come from pardus-xfce-settings, which is not a declared + dependency of this library. Restoring without them would delete the user's + configuration and put nothing back. + """ + return os.path.isdir(DEFAULT_PANEL_DIR) and os.path.isfile(DEFAULT_PANEL_XML) + + def restore_default_settings(): + """Replace the user's panel configuration with the packaged defaults. + + Raises FileNotFoundError if the defaults are missing, before touching + anything the user owns. + """ + if not defaults_available(): + raise FileNotFoundError( + "Default panel configuration not found under /etc/xdg/pardus/xfce4. " + "Is pardus-xfce-settings installed?" + ) + + panel_dir = os.path.expanduser("~/.config/xfce4/panel") + xml_path = os.path.expanduser( + "~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml" + ) + + # The current configuration is about to be removed and cannot be + # reconstructed, so keep a copy next to it. + backup_dir = f"{panel_dir}.bak" + if os.path.isdir(panel_dir): + shutil.rmtree(backup_dir, ignore_errors=True) + shutil.copytree(panel_dir, backup_dir, symlinks=True) + if os.path.isfile(xml_path): + shutil.copy2(xml_path, f"{xml_path}.bak") + xfce4_panel.reset_property("/", True) + + # Chained with && so that a failure stops the sequence instead of carrying + # on to the next step; check=True surfaces it to the caller. subprocess.run( - """xfce4-panel --quit; - pkill xfconfd; - rm -rf ~/.config/xfce4/panel/* ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml; - cp -R /etc/xdg/pardus/xfce4/panel/* ~/.config/xfce4/panel/; - cp /etc/xdg/pardus/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml; - (xfce4-panel &)""", + "xfce4-panel --quit; " + "pkill xfconfd; " + f'rm -rf "{panel_dir}"/* "{xml_path}" && ' + f'cp -R "{DEFAULT_PANEL_DIR}"/* "{panel_dir}/" && ' + f'cp "{DEFAULT_PANEL_XML}" "{xml_path}" && ' + "(xfce4-panel &)", shell=True, + check=True, ) From 244c0102952a112d3909627934433f2920cbaddf Mon Sep 17 00:00:00 2001 From: ehlinazgumus Date: Wed, 29 Jul 2026 13:18:41 +0300 Subject: [PATCH 2/2] fix: do not crash at import when the packaged defaults are missing DatetimeManager copies /etc/xdg/pardus/xfce4/panel/datetime-8.rc into the user's config at module import time, but only checks whether the destination exists - never the source. That file comes from pardus-xfce-settings, which is not a declared dependency of this library, so on a system where it is not installed shutil.copyfile raises FileNotFoundError. Because the copy runs in the module body, the error propagates through pardus-xfce-tweaks' import of this module and the application dies before its window is created. Launched from the menu it fails silently, with nothing shown to the user. Guard the copy on the source existing and treat a missing config as an empty one, so a missing optional package degrades the datetime plugin settings instead of taking down the whole UI. restore_default_settings() had the same unguarded pattern in a worse form: it removed the user's file first and only then copied the default over it, so a missing source left the user with nothing. Check first, and drop the remove - copyfile already overwrites. It also left config_file_data holding the pre-restore contents, which the next save_file() would write straight back out; refresh it. --- src/DatetimeManager.py | 52 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/DatetimeManager.py b/src/DatetimeManager.py index 7b89573..285cc9c 100644 --- a/src/DatetimeManager.py +++ b/src/DatetimeManager.py @@ -8,17 +8,41 @@ PLUGIN_RC_FILE_PATH = f"{HOME_PATH}/.config/xfce4/panel/datetime-8.rc" ETC_XDG_DATETIME_PATH = "/etc/xdg/pardus/xfce4/panel/datetime-8.rc" -# Copy the default config file if not exists: -if not os.path.exists(PLUGIN_RC_FILE_PATH): +def defaults_available(): + """Whether the packaged default plugin configuration is present. + + It comes from pardus-xfce-settings, which is not a declared dependency of + this library, so it can legitimately be missing. + """ + return os.path.exists(ETC_XDG_DATETIME_PATH) + + +def _seed_config_file(): + """Copy the packaged defaults into the user's config, if there is anything + to copy. Missing defaults are not an error here: the plugin settings simply + start out empty.""" + if os.path.exists(PLUGIN_RC_FILE_PATH) or not defaults_available(): + return + # Create directories if not exists os.makedirs(PLUGIN_RC_FILE_DIRECTORY, exist_ok=True) shutil.copyfile(ETC_XDG_DATETIME_PATH, PLUGIN_RC_FILE_PATH) -# Read config file -config_file_data = "" -with open(PLUGIN_RC_FILE_PATH, "r") as file: - config_file_data = file.read() + +def _read_config_file(): + try: + with open(PLUGIN_RC_FILE_PATH, "r") as file: + return file.read() + except FileNotFoundError: + return "" + + +# Seed and read the config file. Neither step may raise: this module is +# imported at application start-up, so a failure here takes down the whole UI +# before it is shown. +_seed_config_file() +config_file_data = _read_config_file() def get(key): @@ -63,9 +87,23 @@ def save_file(): def restore_default_settings(): - os.remove(PLUGIN_RC_FILE_PATH) + global config_file_data + + if not defaults_available(): + raise FileNotFoundError( + f"Default datetime plugin configuration not found at " + f"{ETC_XDG_DATETIME_PATH}. Is pardus-xfce-settings installed?" + ) + + # copyfile overwrites, so there is no need to remove the current file + # first - and removing it first would leave the user with nothing if the + # copy went on to fail. shutil.copyfile(ETC_XDG_DATETIME_PATH, PLUGIN_RC_FILE_PATH) + # Drop the stale in-memory copy, otherwise the next save_file() would write + # the pre-restore configuration back out. + config_file_data = _read_config_file() + def set_panel_clock(key, value): subprocess.call(