feat: implement settings dialog for configuration of authentication, paths, and language

This commit is contained in:
Martin Tranberg
2026-03-31 16:32:26 +02:00
parent 86ff8043f1
commit 8f80611d32

View File

@@ -122,6 +122,21 @@ STRINGS = {
"msg_update_failed_code": "Upload fejlede: {code}",
"msg_unknown_error": "Ukendt fejl",
"type_unknown": "Ukendt",
"btn_settings": "⚙️ Indstillinger",
"settings_title": "Indstillinger",
"settings_auth_group": "Authentication / API",
"settings_client_id": "App (Client) ID:",
"settings_tenant_id": "Tenant ID:",
"settings_path_group": "Systemstier",
"settings_temp_dir": "Midlertidig mappe:",
"settings_app_path": "Applikationssti:",
"settings_active_temp_path": "Aktuel Temp-sti:",
"settings_lang_group": "Sprog / UI",
"settings_language": "Programsprog:",
"settings_save": "Gem indstillinger",
"settings_cancel": "Annuller",
"msg_settings_saved": "Indstillingerne er gemt.",
"msg_restart_required": "Visse ændringer (f.eks. ID'er) træder først i kraft efter genstart.",
"status_login_needed": "Session udløbet. Log ind igen."
},
"en": {
@@ -192,6 +207,21 @@ STRINGS = {
"msg_update_failed_code": "Upload failed: {code}",
"msg_unknown_error": "Unknown error",
"type_unknown": "Unknown",
"btn_settings": "⚙️ Settings",
"settings_title": "Settings",
"settings_auth_group": "Authentication / API",
"settings_client_id": "App (Client) ID:",
"settings_tenant_id": "Tenant ID:",
"settings_path_group": "System Paths",
"settings_temp_dir": "Temporary folder:",
"settings_app_path": "Application path:",
"settings_active_temp_path": "Active Temp path:",
"settings_lang_group": "Language / UI",
"settings_language": "App Language:",
"settings_save": "Save Settings",
"settings_cancel": "Cancel",
"msg_settings_saved": "Settings saved.",
"msg_restart_required": "Some changes (e.g., IDs) only take effect after restart.",
"status_login_needed": "Session expired. Please login again."
}
}
@@ -243,6 +273,117 @@ def format_size(bytes_num):
else:
return f"{bytes_num/(1024**3):.1f} GB"
def is_configured(cfg):
placeholders = ["DIN_CLIENT_ID_HER", "DIN_TENANT_ID_HER", ""]
return cfg.get("client_id") not in placeholders and cfg.get("tenant_id") not in placeholders
class SettingsDialog(wx.Dialog):
def __init__(self, parent, current_settings):
lang = current_settings.get("language", "da")
title = STRINGS[lang].get("settings_title", "Settings")
super().__init__(parent, title=title, size=(520, 620))
self.settings = current_settings.copy()
self.lang = lang
self.InitUI()
def get_txt(self, key):
return STRINGS[self.lang].get(key, key)
def InitUI(self):
vbox = wx.BoxSizer(wx.VERTICAL)
panel = wx.Panel(self)
inner_vbox = wx.BoxSizer(wx.VERTICAL)
# --- Group: Authentication ---
auth_box = wx.StaticBox(panel, label=self.get_txt("settings_auth_group"))
auth_sizer = wx.StaticBoxSizer(auth_box, wx.VERTICAL)
grid = wx.FlexGridSizer(2, 2, 10, 10)
grid.AddGrowableCol(1, 1)
grid.Add(wx.StaticText(panel, label=self.get_txt("settings_client_id")), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
self.client_id_ctrl = wx.TextCtrl(panel, value=self.settings.get("client_id", ""), size=(-1, 25))
grid.Add(self.client_id_ctrl, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
grid.Add(wx.StaticText(panel, label=self.get_txt("settings_tenant_id")), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
self.tenant_id_ctrl = wx.TextCtrl(panel, value=self.settings.get("tenant_id", ""), size=(-1, 25))
grid.Add(self.tenant_id_ctrl, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
auth_sizer.Add(grid, 1, wx.EXPAND | wx.ALL, 10)
inner_vbox.Add(auth_sizer, 0, wx.EXPAND | wx.ALL, 10)
# --- Group: Paths ---
path_box = wx.StaticBox(panel, label=self.get_txt("settings_path_group"))
path_sizer = wx.StaticBoxSizer(path_box, wx.VERTICAL)
path_sizer.Add(wx.StaticText(panel, label=self.get_txt("settings_temp_dir")), 0, wx.BOTTOM, 5)
self.temp_dir_picker = wx.DirPickerCtrl(panel, path=self.settings.get("temp_dir", "C:\\Temp_SP"),
style=wx.DIRP_DIR_MUST_EXIST)
path_sizer.Add(self.temp_dir_picker, 0, wx.EXPAND | wx.BOTTOM, 10)
path_sizer.Add(wx.StaticText(panel, label=self.get_txt("settings_app_path")), 0, wx.BOTTOM, 5)
app_path_box = wx.TextCtrl(panel, value=CONFIG_DIR, style=wx.TE_READONLY | wx.BORDER_NONE)
app_path_box.SetBackgroundColour(panel.GetBackgroundColour())
path_sizer.Add(app_path_box, 0, wx.EXPAND | wx.BOTTOM, 10)
path_sizer.Add(wx.StaticText(panel, label=self.get_txt("settings_active_temp_path")), 0, wx.BOTTOM, 5)
temp_path_box = wx.TextCtrl(panel, value=TEMP_DIR, style=wx.TE_READONLY | wx.BORDER_NONE)
temp_path_box.SetBackgroundColour(panel.GetBackgroundColour())
path_sizer.Add(temp_path_box, 0, wx.EXPAND)
inner_vbox.Add(path_sizer, 0, wx.EXPAND | wx.ALL, 10)
# --- Group: Language ---
lang_box = wx.StaticBox(panel, label=self.get_txt("settings_lang_group"))
lang_sizer = wx.StaticBoxSizer(lang_box, wx.HORIZONTAL)
lang_sizer.Add(wx.StaticText(panel, label=self.get_txt("settings_language")), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
self.lang_choice = wx.Choice(panel, choices=["Dansk", "English"])
self.lang_choice.SetSelection(0 if self.settings.get("language") == "da" else 1)
lang_sizer.Add(self.lang_choice, 1, wx.EXPAND)
inner_vbox.Add(lang_sizer, 0, wx.EXPAND | wx.ALL, 10)
panel.SetSizer(inner_vbox)
inner_vbox.Fit(panel)
vbox.Add(panel, 1, wx.EXPAND | wx.ALL, 0)
# --- Buttons ---
btn_hbox = wx.BoxSizer(wx.HORIZONTAL)
save_btn = wx.Button(self, label=self.get_txt("settings_save"), size=(150, 35))
save_btn.SetBackgroundColour(wx.Colour(0, 120, 215)) # SharePoint Blue
save_btn.SetForegroundColour(wx.WHITE)
save_btn.Bind(wx.EVT_BUTTON, self.on_save)
cancel_btn = wx.Button(self, label=self.get_txt("settings_cancel"), size=(100, 35))
cancel_btn.Bind(wx.EVT_BUTTON, self.on_cancel)
btn_hbox.Add(save_btn, 0, wx.RIGHT, 10)
btn_hbox.Add(cancel_btn, 0)
vbox.Add(btn_hbox, 0, wx.ALIGN_RIGHT | wx.ALL, 15)
self.SetSizer(vbox)
self.Layout()
self.Centre()
def on_save(self, event):
self.settings["client_id"] = self.client_id_ctrl.GetValue().strip()
self.settings["tenant_id"] = self.tenant_id_ctrl.GetValue().strip()
self.settings["temp_dir"] = self.temp_dir_picker.GetPath()
self.settings["language"] = "da" if self.lang_choice.GetSelection() == 0 else "en"
if not self.settings["client_id"] or not self.settings["tenant_id"]:
wx.MessageBox("Client ID og Tenant ID skal udfyldes.", "Fejl", wx.OK | wx.ICON_ERROR)
return
self.EndModal(wx.ID_OK)
def on_cancel(self, event):
self.EndModal(wx.ID_CANCEL)
class SharePointApp(wx.Frame):
def __init__(self):
self.lang = CURRENT_LANG
@@ -283,6 +424,10 @@ class SharePointApp(wx.Frame):
icon_path = os.path.join(RESOURCE_DIR, "icon.ico")
if os.path.exists(icon_path):
self.SetIcon(wx.Icon(icon_path, wx.BITMAP_TYPE_ICO))
# Start indlæsning (Check for konfiguration)
if not is_configured(settings):
wx.CallAfter(self.on_settings_clicked, None)
def get_txt(self, key, **kwargs):
text = STRINGS[self.lang].get(key, key)
if kwargs:
@@ -359,11 +504,10 @@ class SharePointApp(wx.Frame):
self.login_btn.Bind(wx.EVT_BUTTON, self.login)
nav_hbox.Add(self.login_btn, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
# SPROG VÆLGER
self.lang_choice = wx.Choice(nav_panel, choices=["Dansk", "English"])
self.lang_choice.SetSelection(0 if self.lang == "da" else 1)
self.lang_choice.Bind(wx.EVT_CHOICE, self.on_language_changed)
nav_hbox.Add(self.lang_choice, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
# INDSTILLINGER KNAP
self.settings_btn = wx.Button(nav_panel, label=self.get_txt("btn_settings"), size=(120, 30))
self.settings_btn.Bind(wx.EVT_BUTTON, self.on_settings_clicked)
nav_hbox.Add(self.settings_btn, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
nav_panel.SetSizer(nav_hbox)
vbox.Add(nav_panel, 0, wx.EXPAND | wx.ALL, 5)
@@ -724,15 +868,52 @@ class SharePointApp(wx.Frame):
self.PopupMenu(menu)
menu.Destroy()
def on_language_changed(self, event):
selection = self.lang_choice.GetSelection()
self.lang = "da" if selection == 0 else "en"
# Gem til settings
settings["language"] = self.lang
save_settings(settings)
# Opdater UI tekster med det samme
def on_settings_clicked(self, event):
dlg = SettingsDialog(self, settings)
if dlg.ShowModal() == wx.ID_OK:
global CLIENT_ID, TENANT_ID, AUTHORITY, TEMP_DIR, CURRENT_LANG
new_settings = dlg.settings
# Check if IDs changed (need refresh)
ids_changed = (new_settings["client_id"] != settings["client_id"] or
new_settings["tenant_id"] != settings["tenant_id"])
# Save
save_settings(new_settings)
# Update global variables for current session
settings.update(new_settings)
CLIENT_ID = settings["client_id"]
TENANT_ID = settings["tenant_id"]
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
TEMP_DIR = settings["temp_dir"]
# Apply language
if CURRENT_LANG != new_settings["language"]:
CURRENT_LANG = new_settings["language"]
self.lang = CURRENT_LANG
self.refresh_ui_texts()
# Update MSAL App if IDs changed
if ids_changed:
self.msal_app = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY)
self.access_token = None
self.headers = {}
self.login_btn.Enable()
self.login_btn.SetLabel(self.get_txt("btn_login"))
self.login_btn.SetBackgroundColour(wx.Colour(40, 167, 100))
self.show_info(self.get_txt("msg_restart_required"), wx.ICON_INFORMATION)
# Ensure temp dir exists
if not os.path.exists(TEMP_DIR):
os.makedirs(TEMP_DIR)
self.show_info(self.get_txt("msg_settings_saved"))
dlg.Destroy()
def refresh_ui_texts(self):
# Update UI texts for main frame and buttons
self.SetTitle(self.get_txt("title"))
self.back_btn.SetLabel(self.get_txt("btn_back"))
self.home_btn.SetLabel(self.get_txt("btn_home"))
@@ -741,19 +922,14 @@ class SharePointApp(wx.Frame):
self.upload_folder_btn.SetLabel(self.get_txt("btn_upload_folder"))
self.new_folder_btn.SetLabel(self.get_txt("btn_new_folder"))
self.refresh_btn.SetLabel(self.get_txt("btn_refresh"))
self.settings_btn.SetLabel(self.get_txt("btn_settings"))
if self.access_token:
self.login_btn.SetLabel(self.get_txt("btn_logged_in"))
else:
self.login_btn.SetLabel(self.get_txt("btn_login"))
# Opdater kolonner i ListCtrl
self.list_ctrl.SetColumnWidth(0, 450) # Refresh widths
item = self.list_ctrl.GetColumn(0)
self.list_ctrl.SetColumn(0, wx.ListItem()) # Reset column header logic could be complex in wx,
# but the simplest is to just re-insert columns or set text
# Re-set headers (Fix: explicitly set image to -1 to avoid icons in headers)
# Re-set headers for ListCtrl
cols = [self.get_txt("col_name"), self.get_txt("col_type"), self.get_txt("col_size"), self.get_txt("col_modified")]
for i, text in enumerate(cols):
info = self.list_ctrl.GetColumn(i)
@@ -762,7 +938,11 @@ class SharePointApp(wx.Frame):
self.list_ctrl.SetColumn(i, info)
self.set_status(self.get_txt("status_ready"))
self._refresh_current_view() # Gendanner list-item tekster (Mappe/Fil)
self._refresh_current_view()
def on_language_changed(self, event):
# Deprecated: use on_settings_clicked instead if you want or keep for quick switch
pass # We'll just remove or redirect it later
def on_close_window(self, event):
if self.active_edits: