From 0683b2753410278e8fb0a526e36ff02a24f8a9b7 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 13 Apr 2025 05:51:19 -0400 Subject: [PATCH] New Admin section Header Settings to change Header Title. --- CHANGELOG.md | 4 +- getConfig.php | 12 ++++- js/auth.js | 18 +++++++ js/authModals.js | 131 +++++++++++++++++++++++++++++++++++++++++------ updateConfig.php | 11 ++-- 5 files changed, 151 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b49ce..ba882c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Changelog -## Changes 4//13/2025 +## Changes 4/13/2025 - Decreased header height some more and clickable logo. - authModals.js fully updated with i18n.js keys. - main.js added Dark & Light mode i18n.js keys. +- New Admin section Header Settings to change Header Title. +- Admin Panel confirm unsaved changes. ## Changes 4/12/2025 diff --git a/getConfig.php b/getConfig.php index b8f5e3f..a9ca029 100644 --- a/getConfig.php +++ b/getConfig.php @@ -11,14 +11,24 @@ if (file_exists($configFile)) { echo json_encode(['error' => 'Failed to decrypt configuration.']); exit; } - // Decode the configuration and ensure globalOtpauthUrl is set + // Decode the configuration and ensure required fields are set $config = json_decode($decryptedContent, true); + + // Ensure globalOtpauthUrl is set if (!isset($config['globalOtpauthUrl'])) { $config['globalOtpauthUrl'] = ""; } + + // NEW: Ensure header_title is set. + if (!isset($config['header_title']) || empty($config['header_title'])) { + $config['header_title'] = "FileRise"; // default value + } + echo json_encode($config); } else { + // If no config file exists, provide defaults echo json_encode([ + 'header_title' => "FileRise", 'oidc' => [ 'providerUrl' => 'https://your-oidc-provider.com', 'clientId' => 'YOUR_CLIENT_ID', diff --git a/js/auth.js b/js/auth.js index d9a8da2..7e838ab 100644 --- a/js/auth.js +++ b/js/auth.js @@ -97,18 +97,36 @@ function loadAdminConfigFunc() { return fetch("getConfig.php", { credentials: "include" }) .then(response => response.json()) .then(config => { + // Save header_title into localStorage (if needed) + localStorage.setItem("headerTitle", config.header_title || "FileRise"); + + // Update login options and global OTPAuth URL as before localStorage.setItem("disableFormLogin", config.loginOptions.disableFormLogin); localStorage.setItem("disableBasicAuth", config.loginOptions.disableBasicAuth); localStorage.setItem("disableOIDCLogin", config.loginOptions.disableOIDCLogin); localStorage.setItem("globalOtpauthUrl", config.globalOtpauthUrl || "otpauth://totp/{label}?secret={secret}&issuer=FileRise"); + + // Update the UI for login options updateLoginOptionsUIFromStorage(); + + const headerTitleElem = document.querySelector(".header-title h1"); + if (headerTitleElem) { + headerTitleElem.textContent = config.header_title || "FileRise"; + } }) .catch(() => { + // Fallback defaults in case of error + localStorage.setItem("headerTitle", "FileRise"); localStorage.setItem("disableFormLogin", "false"); localStorage.setItem("disableBasicAuth", "false"); localStorage.setItem("disableOIDCLogin", "false"); localStorage.setItem("globalOtpauthUrl", "otpauth://totp/{label}?secret={secret}&issuer=FileRise"); updateLoginOptionsUIFromStorage(); + + const headerTitleElem = document.querySelector(".header-title h1"); + if (headerTitleElem) { + headerTitleElem.textContent = "FileRise"; + } }); } diff --git a/js/authModals.js b/js/authModals.js index b651a78..5060d14 100644 --- a/js/authModals.js +++ b/js/authModals.js @@ -487,10 +487,81 @@ export function closeTOTPModal(disable = true) { } } +// Global variable to hold the initial state of the admin form. +let originalAdminConfig = {}; + +// Capture the initial state of the admin form fields. +function captureInitialAdminConfig() { + originalAdminConfig = { + headerTitle: document.getElementById("headerTitle").value.trim(), + oidcProviderUrl: document.getElementById("oidcProviderUrl").value.trim(), + oidcClientId: document.getElementById("oidcClientId").value.trim(), + oidcClientSecret: document.getElementById("oidcClientSecret").value.trim(), + oidcRedirectUri: document.getElementById("oidcRedirectUri").value.trim(), + disableFormLogin: document.getElementById("disableFormLogin").checked, + disableBasicAuth: document.getElementById("disableBasicAuth").checked, + disableOIDCLogin: document.getElementById("disableOIDCLogin").checked, + globalOtpauthUrl: document.getElementById("globalOtpauthUrl").value.trim() + }; +} + +// Compare current values to the captured initial state. +function hasUnsavedChanges() { + return ( + document.getElementById("headerTitle").value.trim() !== originalAdminConfig.headerTitle || + document.getElementById("oidcProviderUrl").value.trim() !== originalAdminConfig.oidcProviderUrl || + document.getElementById("oidcClientId").value.trim() !== originalAdminConfig.oidcClientId || + document.getElementById("oidcClientSecret").value.trim() !== originalAdminConfig.oidcClientSecret || + document.getElementById("oidcRedirectUri").value.trim() !== originalAdminConfig.oidcRedirectUri || + document.getElementById("disableFormLogin").checked !== originalAdminConfig.disableFormLogin || + document.getElementById("disableBasicAuth").checked !== originalAdminConfig.disableBasicAuth || + document.getElementById("disableOIDCLogin").checked !== originalAdminConfig.disableOIDCLogin || + document.getElementById("globalOtpauthUrl").value.trim() !== originalAdminConfig.globalOtpauthUrl + ); +} + +// Use your custom confirmation modal. +function showCustomConfirmModal(message) { + return new Promise((resolve) => { + // Get modal elements from DOM. + const modal = document.getElementById("customConfirmModal"); + const messageElem = document.getElementById("confirmMessage"); + const yesBtn = document.getElementById("confirmYesBtn"); + const noBtn = document.getElementById("confirmNoBtn"); + + // Set the message in the modal. + messageElem.textContent = message; + modal.style.display = "block"; + + // Define event handlers. + function onYes() { + cleanup(); + resolve(true); + } + function onNo() { + cleanup(); + resolve(false); + } + // Remove event listeners and hide modal after choice. + function cleanup() { + yesBtn.removeEventListener("click", onYes); + noBtn.removeEventListener("click", onNo); + modal.style.display = "none"; + } + + yesBtn.addEventListener("click", onYes); + noBtn.addEventListener("click", onNo); + }); +} + export function openAdminPanel() { fetch("getConfig.php", { credentials: "include" }) .then(response => response.json()) .then(config => { + if (config.header_title) { + document.querySelector(".header-title h1").textContent = config.header_title; + window.headerTitle = config.header_title || "FileRise"; + } if (config.oidc) Object.assign(window.currentOIDCConfig, config.oidc); if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl; const isDarkMode = document.body.classList.contains("dark-mode"); @@ -537,6 +608,28 @@ export function openAdminPanel() { +
+ Header Settings +
+ + +
+
+
+ ${t("login_options")} +
+ + +
+
+ + +
+
+ + +
+
${t("oidc_configuration")}
@@ -563,21 +656,6 @@ export function openAdminPanel() {
-
- ${t("login_options")} -
- - -
-
- - -
-
- - -
-
@@ -587,11 +665,14 @@ export function openAdminPanel() { `; document.body.appendChild(adminModal); + // Bind closing events that will use our enhanced close function. document.getElementById("closeAdminPanel").addEventListener("click", closeAdminPanel); adminModal.addEventListener("click", (e) => { if (e.target === adminModal) closeAdminPanel(); }); document.getElementById("cancelAdminSettings").addEventListener("click", closeAdminPanel); + + // Bind other buttons. document.getElementById("adminOpenAddUser").addEventListener("click", () => { toggleVisibility("addUserModal", true); document.getElementById("newUsername").focus(); @@ -602,7 +683,6 @@ export function openAdminPanel() { } toggleVisibility("removeUserModal", true); }); - // New event binding for the User Permissions button: document.getElementById("adminOpenUserPermissions").addEventListener("click", () => { openUserPermissionsModal(); }); @@ -624,6 +704,7 @@ export function openAdminPanel() { } return; } + const newHeaderTitle = document.getElementById("headerTitle").value.trim(); const newOIDCConfig = { providerUrl: document.getElementById("oidcProviderUrl").value.trim(), clientId: document.getElementById("oidcClientId").value.trim(), @@ -635,6 +716,7 @@ export function openAdminPanel() { const disableOIDCLogin = disableOIDCLoginCheckbox.checked; const globalOtpauthUrl = document.getElementById("globalOtpauthUrl").value.trim(); sendRequest("updateConfig.php", "POST", { + header_title: newHeaderTitle, oidc: newOIDCConfig, disableFormLogin, disableBasicAuth, @@ -650,13 +732,17 @@ export function openAdminPanel() { if (typeof window.updateLoginOptionsUI === "function") { window.updateLoginOptionsUI({ disableFormLogin, disableBasicAuth, disableOIDCLogin }); } + // Update the captured initial state since the changes have now been saved. + captureInitialAdminConfig(); closeAdminPanel(); + } else { showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error"))); } }) .catch(() => { }); }); + // Enforce login option constraints. const disableFormLoginCheckbox = document.getElementById("disableFormLogin"); const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth"); const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin"); @@ -674,6 +760,9 @@ export function openAdminPanel() { document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; + + // Capture initial state after the modal loads. + captureInitialAdminConfig(); } else { adminModal.style.backgroundColor = overlayBackground; const modalContent = adminModal.querySelector(".modal-content"); @@ -691,6 +780,7 @@ export function openAdminPanel() { document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; adminModal.style.display = "flex"; + captureInitialAdminConfig(); } }) .catch(() => { @@ -712,13 +802,20 @@ export function openAdminPanel() { document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true"; document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true"; adminModal.style.display = "flex"; + captureInitialAdminConfig(); } else { openAdminPanel(); } }); } -export function closeAdminPanel() { +export async function closeAdminPanel() { + if (hasUnsavedChanges()) { + const userConfirmed = await showCustomConfirmModal("You have unsaved changes. Are you sure you want to close without saving?"); + if (!userConfirmed) { + return; + } + } const adminModal = document.getElementById("adminPanelModal"); if (adminModal) adminModal.style.display = "none"; } diff --git a/updateConfig.php b/updateConfig.php index 54e9808..de8d0bf 100644 --- a/updateConfig.php +++ b/updateConfig.php @@ -33,6 +33,9 @@ if (!is_array($data)) { exit; } +// Retrieve new header title, sanitize if necessary. +$headerTitle = isset($data['header_title']) ? trim($data['header_title']) : ""; + // Validate and sanitize OIDC configuration. $oidc = isset($data['oidc']) ? $data['oidc'] : []; $oidcProviderUrl = isset($oidc['providerUrl']) ? filter_var($oidc['providerUrl'], FILTER_SANITIZE_URL) : ''; @@ -54,8 +57,9 @@ $disableOIDCLogin = isset($data['disableOIDCLogin']) ? filter_var($data['disable // Retrieve the global OTPAuth URL (new field). If not provided, default to an empty string. $globalOtpauthUrl = isset($data['globalOtpauthUrl']) ? trim($data['globalOtpauthUrl']) : ""; -// Prepare configuration array. +// Prepare configuration array including the header title. $configUpdate = [ + 'header_title' => $headerTitle, // New field for the header title 'oidc' => [ 'providerUrl' => $oidcProviderUrl, 'clientId' => $oidcClientId, @@ -79,15 +83,10 @@ $encryptedContent = encryptData($plainTextConfig, $encryptionKey); // Attempt to write the new configuration. if (file_put_contents($configFile, $encryptedContent, LOCK_EX) === false) { - // Log the error. error_log("updateConfig.php: Initial write failed, attempting to delete the old configuration file."); - - // Delete the old file. if (file_exists($configFile)) { unlink($configFile); } - - // Try writing again. if (file_put_contents($configFile, $encryptedContent, LOCK_EX) === false) { error_log("updateConfig.php: Failed to write configuration even after deletion."); http_response_code(500);