New Admin section Header Settings to change Header Title.

This commit is contained in:
Ryan
2025-04-13 05:51:19 -04:00
committed by GitHub
parent 49c42e8096
commit 0683b27534
5 changed files with 151 additions and 25 deletions

View File

@@ -1,10 +1,12 @@
# Changelog # Changelog
## Changes 4//13/2025 ## Changes 4/13/2025
- Decreased header height some more and clickable logo. - Decreased header height some more and clickable logo.
- authModals.js fully updated with i18n.js keys. - authModals.js fully updated with i18n.js keys.
- main.js added Dark & Light mode 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 ## Changes 4/12/2025

View File

@@ -11,14 +11,24 @@ if (file_exists($configFile)) {
echo json_encode(['error' => 'Failed to decrypt configuration.']); echo json_encode(['error' => 'Failed to decrypt configuration.']);
exit; exit;
} }
// Decode the configuration and ensure globalOtpauthUrl is set // Decode the configuration and ensure required fields are set
$config = json_decode($decryptedContent, true); $config = json_decode($decryptedContent, true);
// Ensure globalOtpauthUrl is set
if (!isset($config['globalOtpauthUrl'])) { if (!isset($config['globalOtpauthUrl'])) {
$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); echo json_encode($config);
} else { } else {
// If no config file exists, provide defaults
echo json_encode([ echo json_encode([
'header_title' => "FileRise",
'oidc' => [ 'oidc' => [
'providerUrl' => 'https://your-oidc-provider.com', 'providerUrl' => 'https://your-oidc-provider.com',
'clientId' => 'YOUR_CLIENT_ID', 'clientId' => 'YOUR_CLIENT_ID',

View File

@@ -97,18 +97,36 @@ function loadAdminConfigFunc() {
return fetch("getConfig.php", { credentials: "include" }) return fetch("getConfig.php", { credentials: "include" })
.then(response => response.json()) .then(response => response.json())
.then(config => { .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("disableFormLogin", config.loginOptions.disableFormLogin);
localStorage.setItem("disableBasicAuth", config.loginOptions.disableBasicAuth); localStorage.setItem("disableBasicAuth", config.loginOptions.disableBasicAuth);
localStorage.setItem("disableOIDCLogin", config.loginOptions.disableOIDCLogin); localStorage.setItem("disableOIDCLogin", config.loginOptions.disableOIDCLogin);
localStorage.setItem("globalOtpauthUrl", config.globalOtpauthUrl || "otpauth://totp/{label}?secret={secret}&issuer=FileRise"); localStorage.setItem("globalOtpauthUrl", config.globalOtpauthUrl || "otpauth://totp/{label}?secret={secret}&issuer=FileRise");
// Update the UI for login options
updateLoginOptionsUIFromStorage(); updateLoginOptionsUIFromStorage();
const headerTitleElem = document.querySelector(".header-title h1");
if (headerTitleElem) {
headerTitleElem.textContent = config.header_title || "FileRise";
}
}) })
.catch(() => { .catch(() => {
// Fallback defaults in case of error
localStorage.setItem("headerTitle", "FileRise");
localStorage.setItem("disableFormLogin", "false"); localStorage.setItem("disableFormLogin", "false");
localStorage.setItem("disableBasicAuth", "false"); localStorage.setItem("disableBasicAuth", "false");
localStorage.setItem("disableOIDCLogin", "false"); localStorage.setItem("disableOIDCLogin", "false");
localStorage.setItem("globalOtpauthUrl", "otpauth://totp/{label}?secret={secret}&issuer=FileRise"); localStorage.setItem("globalOtpauthUrl", "otpauth://totp/{label}?secret={secret}&issuer=FileRise");
updateLoginOptionsUIFromStorage(); updateLoginOptionsUIFromStorage();
const headerTitleElem = document.querySelector(".header-title h1");
if (headerTitleElem) {
headerTitleElem.textContent = "FileRise";
}
}); });
} }

View File

@@ -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() { export function openAdminPanel() {
fetch("getConfig.php", { credentials: "include" }) fetch("getConfig.php", { credentials: "include" })
.then(response => response.json()) .then(response => response.json())
.then(config => { .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.oidc) Object.assign(window.currentOIDCConfig, config.oidc);
if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl; if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl;
const isDarkMode = document.body.classList.contains("dark-mode"); const isDarkMode = document.body.classList.contains("dark-mode");
@@ -537,6 +608,28 @@ export function openAdminPanel() {
<button type="button" id="adminOpenUserPermissions" class="btn btn-secondary">${t("user_permissions")}</button> <button type="button" id="adminOpenUserPermissions" class="btn btn-secondary">${t("user_permissions")}</button>
</div> </div>
</fieldset> </fieldset>
<fieldset style="margin-bottom: 15px;">
<legend>Header Settings</legend>
<div class="form-group">
<label for="headerTitle">Header Title:</label>
<input type="text" id="headerTitle" class="form-control" value="${window.headerTitle}" />
</div>
</fieldset>
<fieldset style="margin-bottom: 15px;">
<legend>${t("login_options")}</legend>
<div class="form-group">
<input type="checkbox" id="disableFormLogin" />
<label for="disableFormLogin">${t("disable_login_form")}</label>
</div>
<div class="form-group">
<input type="checkbox" id="disableBasicAuth" />
<label for="disableBasicAuth">${t("disable_basic_http_auth")}</label>
</div>
<div class="form-group">
<input type="checkbox" id="disableOIDCLogin" />
<label for="disableOIDCLogin">${t("disable_oidc_login")}</label>
</div>
</fieldset>
<fieldset style="margin-bottom: 15px;"> <fieldset style="margin-bottom: 15px;">
<legend>${t("oidc_configuration")}</legend> <legend>${t("oidc_configuration")}</legend>
<div class="form-group"> <div class="form-group">
@@ -563,21 +656,6 @@ export function openAdminPanel() {
<input type="text" id="globalOtpauthUrl" class="form-control" value="${window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise'}" /> <input type="text" id="globalOtpauthUrl" class="form-control" value="${window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise'}" />
</div> </div>
</fieldset> </fieldset>
<fieldset style="margin-bottom: 15px;">
<legend>${t("login_options")}</legend>
<div class="form-group">
<input type="checkbox" id="disableFormLogin" />
<label for="disableFormLogin">${t("disable_login_form")}</label>
</div>
<div class="form-group">
<input type="checkbox" id="disableBasicAuth" />
<label for="disableBasicAuth">${t("disable_basic_http_auth")}</label>
</div>
<div class="form-group">
<input type="checkbox" id="disableOIDCLogin" />
<label for="disableOIDCLogin">${t("disable_oidc_login")}</label>
</div>
</fieldset>
<div style="display: flex; justify-content: space-between;"> <div style="display: flex; justify-content: space-between;">
<button type="button" id="cancelAdminSettings" class="btn btn-secondary">${t("cancel")}</button> <button type="button" id="cancelAdminSettings" class="btn btn-secondary">${t("cancel")}</button>
<button type="button" id="saveAdminSettings" class="btn btn-primary">${t("save_settings")}</button> <button type="button" id="saveAdminSettings" class="btn btn-primary">${t("save_settings")}</button>
@@ -587,11 +665,14 @@ export function openAdminPanel() {
`; `;
document.body.appendChild(adminModal); document.body.appendChild(adminModal);
// Bind closing events that will use our enhanced close function.
document.getElementById("closeAdminPanel").addEventListener("click", closeAdminPanel); document.getElementById("closeAdminPanel").addEventListener("click", closeAdminPanel);
adminModal.addEventListener("click", (e) => { adminModal.addEventListener("click", (e) => {
if (e.target === adminModal) closeAdminPanel(); if (e.target === adminModal) closeAdminPanel();
}); });
document.getElementById("cancelAdminSettings").addEventListener("click", closeAdminPanel); document.getElementById("cancelAdminSettings").addEventListener("click", closeAdminPanel);
// Bind other buttons.
document.getElementById("adminOpenAddUser").addEventListener("click", () => { document.getElementById("adminOpenAddUser").addEventListener("click", () => {
toggleVisibility("addUserModal", true); toggleVisibility("addUserModal", true);
document.getElementById("newUsername").focus(); document.getElementById("newUsername").focus();
@@ -602,7 +683,6 @@ export function openAdminPanel() {
} }
toggleVisibility("removeUserModal", true); toggleVisibility("removeUserModal", true);
}); });
// New event binding for the User Permissions button:
document.getElementById("adminOpenUserPermissions").addEventListener("click", () => { document.getElementById("adminOpenUserPermissions").addEventListener("click", () => {
openUserPermissionsModal(); openUserPermissionsModal();
}); });
@@ -624,6 +704,7 @@ export function openAdminPanel() {
} }
return; return;
} }
const newHeaderTitle = document.getElementById("headerTitle").value.trim();
const newOIDCConfig = { const newOIDCConfig = {
providerUrl: document.getElementById("oidcProviderUrl").value.trim(), providerUrl: document.getElementById("oidcProviderUrl").value.trim(),
clientId: document.getElementById("oidcClientId").value.trim(), clientId: document.getElementById("oidcClientId").value.trim(),
@@ -635,6 +716,7 @@ export function openAdminPanel() {
const disableOIDCLogin = disableOIDCLoginCheckbox.checked; const disableOIDCLogin = disableOIDCLoginCheckbox.checked;
const globalOtpauthUrl = document.getElementById("globalOtpauthUrl").value.trim(); const globalOtpauthUrl = document.getElementById("globalOtpauthUrl").value.trim();
sendRequest("updateConfig.php", "POST", { sendRequest("updateConfig.php", "POST", {
header_title: newHeaderTitle,
oidc: newOIDCConfig, oidc: newOIDCConfig,
disableFormLogin, disableFormLogin,
disableBasicAuth, disableBasicAuth,
@@ -650,13 +732,17 @@ export function openAdminPanel() {
if (typeof window.updateLoginOptionsUI === "function") { if (typeof window.updateLoginOptionsUI === "function") {
window.updateLoginOptionsUI({ disableFormLogin, disableBasicAuth, disableOIDCLogin }); window.updateLoginOptionsUI({ disableFormLogin, disableBasicAuth, disableOIDCLogin });
} }
// Update the captured initial state since the changes have now been saved.
captureInitialAdminConfig();
closeAdminPanel(); closeAdminPanel();
} else { } else {
showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error"))); showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error")));
} }
}) })
.catch(() => { }); .catch(() => { });
}); });
// Enforce login option constraints.
const disableFormLoginCheckbox = document.getElementById("disableFormLogin"); const disableFormLoginCheckbox = document.getElementById("disableFormLogin");
const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth"); const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth");
const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin"); const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin");
@@ -674,6 +760,9 @@ export function openAdminPanel() {
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true; document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
// Capture initial state after the modal loads.
captureInitialAdminConfig();
} else { } else {
adminModal.style.backgroundColor = overlayBackground; adminModal.style.backgroundColor = overlayBackground;
const modalContent = adminModal.querySelector(".modal-content"); const modalContent = adminModal.querySelector(".modal-content");
@@ -691,6 +780,7 @@ export function openAdminPanel() {
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
adminModal.style.display = "flex"; adminModal.style.display = "flex";
captureInitialAdminConfig();
} }
}) })
.catch(() => { .catch(() => {
@@ -712,13 +802,20 @@ export function openAdminPanel() {
document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true"; document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true";
document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true"; document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true";
adminModal.style.display = "flex"; adminModal.style.display = "flex";
captureInitialAdminConfig();
} else { } else {
openAdminPanel(); 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"); const adminModal = document.getElementById("adminPanelModal");
if (adminModal) adminModal.style.display = "none"; if (adminModal) adminModal.style.display = "none";
} }

View File

@@ -33,6 +33,9 @@ if (!is_array($data)) {
exit; exit;
} }
// Retrieve new header title, sanitize if necessary.
$headerTitle = isset($data['header_title']) ? trim($data['header_title']) : "";
// Validate and sanitize OIDC configuration. // Validate and sanitize OIDC configuration.
$oidc = isset($data['oidc']) ? $data['oidc'] : []; $oidc = isset($data['oidc']) ? $data['oidc'] : [];
$oidcProviderUrl = isset($oidc['providerUrl']) ? filter_var($oidc['providerUrl'], FILTER_SANITIZE_URL) : ''; $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. // Retrieve the global OTPAuth URL (new field). If not provided, default to an empty string.
$globalOtpauthUrl = isset($data['globalOtpauthUrl']) ? trim($data['globalOtpauthUrl']) : ""; $globalOtpauthUrl = isset($data['globalOtpauthUrl']) ? trim($data['globalOtpauthUrl']) : "";
// Prepare configuration array. // Prepare configuration array including the header title.
$configUpdate = [ $configUpdate = [
'header_title' => $headerTitle, // New field for the header title
'oidc' => [ 'oidc' => [
'providerUrl' => $oidcProviderUrl, 'providerUrl' => $oidcProviderUrl,
'clientId' => $oidcClientId, 'clientId' => $oidcClientId,
@@ -79,15 +83,10 @@ $encryptedContent = encryptData($plainTextConfig, $encryptionKey);
// Attempt to write the new configuration. // Attempt to write the new configuration.
if (file_put_contents($configFile, $encryptedContent, LOCK_EX) === false) { 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."); error_log("updateConfig.php: Initial write failed, attempting to delete the old configuration file.");
// Delete the old file.
if (file_exists($configFile)) { if (file_exists($configFile)) {
unlink($configFile); unlink($configFile);
} }
// Try writing again.
if (file_put_contents($configFile, $encryptedContent, LOCK_EX) === false) { if (file_put_contents($configFile, $encryptedContent, LOCK_EX) === false) {
error_log("updateConfig.php: Failed to write configuration even after deletion."); error_log("updateConfig.php: Failed to write configuration even after deletion.");
http_response_code(500); http_response_code(500);