feat: add open in browser context menu option and optimize MSAL authentication handling

This commit is contained in:
Martin Tranberg
2026-03-31 11:35:55 +02:00
parent ed931f2088
commit b1c46fbace

View File

@@ -8,6 +8,7 @@ import requests
import msal
import wx
import wx.lib.newevent
import webbrowser
# --- STIHÅNDTERING (Til EXE-brug) ---
if getattr(sys, 'frozen', False):
@@ -85,6 +86,7 @@ STRINGS = {
"msg_rename": "Omdøb",
"msg_rename_prompt": "Indtast det nye navn for '{name}':",
"msg_rename_title": "Omdøb emne",
"msg_open_browser": "Åbn i browser",
"msg_upload_here": "Upload fil her",
"msg_upload_folder_here": "Upload mappe her",
"msg_new_folder_here": "Opret ny mappe her",
@@ -113,7 +115,8 @@ STRINGS = {
"msg_update_success": "Succes! '{name}' er opdateret.",
"msg_update_failed_code": "Upload fejlede: {code}",
"msg_unknown_error": "Ukendt fejl",
"type_unknown": "Ukendt"
"type_unknown": "Ukendt",
"status_login_needed": "Session udløbet. Log ind igen."
},
"en": {
"title": "SharePoint Explorer",
@@ -149,6 +152,7 @@ STRINGS = {
"msg_rename": "Rename",
"msg_rename_prompt": "Enter new name for '{name}':",
"msg_rename_title": "Rename item",
"msg_open_browser": "Open in browser",
"msg_upload_here": "Upload file here",
"msg_upload_folder_here": "Upload folder here",
"msg_new_folder_here": "Create new folder here",
@@ -177,7 +181,8 @@ STRINGS = {
"msg_update_success": "Success! '{name}' has been updated.",
"msg_update_failed_code": "Upload failed: {code}",
"msg_unknown_error": "Unknown error",
"type_unknown": "Unknown"
"type_unknown": "Unknown",
"status_login_needed": "Session expired. Please login again."
}
}
@@ -257,6 +262,9 @@ class SharePointApp(wx.Frame):
# Threading/Sync til filredigering
self.edit_wait_event = threading.Event()
# MSAL Cache
self.msal_app = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY)
self.InitUI()
self.Centre()
self.Show()
@@ -400,6 +408,12 @@ class SharePointApp(wx.Frame):
if len(selected_indices) == 1:
item = selected_items[0]
if item.get("web_url"):
browser_item = menu.Append(wx.ID_ANY, self.get_txt("msg_open_browser"))
self.Bind(wx.EVT_MENU, lambda e: webbrowser.open(item["web_url"]), browser_item)
menu.AppendSeparator()
if item['type'] == "FILE":
edit_item = menu.Append(wx.ID_ANY, self.get_txt("msg_edit_file"))
self.Bind(wx.EVT_MENU, lambda e: threading.Thread(target=self.process_file, args=(item['id'], item['name']), daemon=True).start(), edit_item)
@@ -707,42 +721,44 @@ class SharePointApp(wx.Frame):
def ensure_valid_token(self):
"""Sikrer at vi har et gyldigt token. Returnerer True hvis OK."""
try:
app = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY)
accounts = app.get_accounts()
accounts = self.msal_app.get_accounts()
if not accounts:
self.set_status(self.get_txt("status_login_needed"))
return False
result = app.acquire_token_silent(SCOPES, account=accounts[0])
result = self.msal_app.acquire_token_silent(SCOPES, account=accounts[0])
if result and "access_token" in result:
self.access_token = result["access_token"]
self.headers = {'Authorization': f'Bearer {self.access_token}'}
return True
except:
pass
except Exception as e:
print(f"Token refresh error: {e}")
self.set_status(self.get_txt("status_login_needed"))
return False
def login(self, event):
self.set_status(self.get_txt("status_logging_in"))
app = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY)
accounts = app.get_accounts()
accounts = self.msal_app.get_accounts()
result = None
if accounts:
result = app.acquire_token_silent(SCOPES, account=accounts[0])
result = self.msal_app.acquire_token_silent(SCOPES, account=accounts[0])
if not result or "access_token" not in result:
result = app.acquire_token_interactive(scopes=SCOPES)
result = self.msal_app.acquire_token_interactive(scopes=SCOPES)
if "access_token" in result:
self.access_token = result["access_token"]
self.headers = {'Authorization': f'Bearer {self.access_token}'}
self.login_btn.Disable()
# self.login_btn.Hide() # Valgfrit: Skjul login knap helt når vi er inde
self.login_btn.SetLabel(self.get_txt("btn_logged_in"))
self.login_btn.SetBackgroundColour(wx.Colour(200, 200, 200)) # Grå
self.home_btn.Enable()
self.load_sites()
else:
self.set_status(self.get_txt("status_login_failed"))
wx.MessageBox(result.get("error_description", self.get_txt("msg_unknown_error")), self.get_txt("msg_error"), wx.OK | wx.ICON_ERROR)
wx.CallAfter(wx.MessageBox, result.get("error_description", self.get_txt("msg_unknown_error")), self.get_txt("msg_error"), wx.OK | wx.ICON_ERROR)
def load_sites(self, event=None):
self.set_status(self.get_txt("status_fetching_sites"))
@@ -790,7 +806,10 @@ class SharePointApp(wx.Frame):
self.list_ctrl.SetItem(i, 1, self.get_txt("type_site"))
self.list_ctrl.SetItem(i, 2, "") # Størrelse
self.list_ctrl.SetItem(i, 3, "") # Sidst ændret
self.current_items.append({"type": "SITE", "id": site['id'], "name": name, "size": None, "modified": ""})
self.current_items.append({
"type": "SITE", "id": site['id'], "name": name,
"size": None, "modified": "", "web_url": site.get('webUrl')
})
def on_tree_expanding(self, event):
item = event.GetItem()
@@ -907,7 +926,8 @@ class SharePointApp(wx.Frame):
for drive in drives:
items_data.append({
"type": "DRIVE", "id": drive['id'], "name": drive.get('name', ''),
"drive_id": drive['id'], "modified": "", "size": None
"drive_id": drive['id'], "modified": "", "size": None,
"web_url": drive.get('webUrl')
})
elif data['type'] in ["DRIVE", "FOLDER"]:
drive_id = data['drive_id']
@@ -927,7 +947,8 @@ class SharePointApp(wx.Frame):
"type": "FOLDER" if is_folder else "FILE",
"id": item['id'], "name": item['name'],
"drive_id": drive_id, "modified": modified,
"size": item.get('size') if not is_folder else None
"size": item.get('size') if not is_folder else None,
"web_url": item.get('webUrl')
})
wx.CallAfter(self._populate_list_ctrl, items_data, data)