diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d41d97..55d3c7e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -77,6 +77,28 @@
- `#viewSliderContainer` uses `inline-flex` and `align-items: center` so that label, slider, and value text are vertically aligned with the other toolbar elements.
- Reset margins/padding on the label and value span within `#viewSliderContainer` to eliminate any vertical misalignment.
+### 9. Fixed new issues with Undefined username in header on profile pic change & TOTP Enabled not checked
+
+**openUserPanel**
+
+- **Rewritten entirely with DOM APIs** instead of `innerHTML` for any user-supplied text to eliminates “DOM text reinterpreted as HTML” warnings.
+- **Default avatar fallback**: now uses `'/assets/default-avatar.png'` whenever `profile_picture` is empty.
+- **TOTP checkbox initial state** is now set from the `totp_enabled` value returned by the server.
+- **Modal title sync** on reopen now updates the `(username)` correctly (no more “undefined” until refresh).
+- **Re-sync on reopen**: background color, avatar, TOTP checkbox and language selector all update when reopen the panel.
+
+**updateAuthenticatedUI**
+
+- **Username fix**: dropdown toggle now always uses `data.username` so the name never becomes `undefined` after uploading a picture.
+- **Profile URL update** via `fetchProfilePicture()` always writes into `localStorage` before rebuilding the header, ensuring avatar+name stay in sync instantly.
+- **Dropdown rebuild logic** tweaked to update the toggle’s innerHTML with both avatar and username on every call.
+
+**UserModel::getUser**
+
+- Switched to `explode(':', $line, 4)` to the fourth “profile_picture” field without clobbering the TOTP secret.
+- **Strip trailing colons** from the stored URL (`rtrim($parts[3], ':')`) so we never send `…png:` back to the client.
+- Returns an array with both `'username'` and `'profile_picture'`, matching what `getCurrentUser.php` needs.
+
---
## Changes 5/8/2025
diff --git a/public/api/profile/getCurrentUser.php b/public/api/profile/getCurrentUser.php
index c60fd6c..9439059 100644
--- a/public/api/profile/getCurrentUser.php
+++ b/public/api/profile/getCurrentUser.php
@@ -1,13 +1,13 @@
'Unauthorized']);
- exit;
+ http_response_code(401);
+ echo json_encode(['error'=>'Unauthorized']);
+ exit;
}
$user = $_SESSION['username'];
diff --git a/public/js/auth.js b/public/js/auth.js
index 2ad7f6b..cde8499 100644
--- a/public/js/auth.js
+++ b/public/js/auth.js
@@ -223,6 +223,9 @@ async function fetchProfilePicture() {
}
export async function updateAuthenticatedUI(data) {
+ // Save latest auth data for later reuse
+ window.__lastAuthData = data;
+
// 1) Remove loading overlay safely
const loading = document.getElementById('loadingOverlay');
if (loading) loading.remove();
@@ -304,6 +307,11 @@ export async function updateAuthenticatedUI(data) {
? ``
: `account_circle`;
+ // fallback username if missing
+ const usernameText = data.username
+ || localStorage.getItem("username")
+ || "";
+
if (!dd) {
dd = document.createElement("div");
dd.id = "userDropdown";
@@ -314,7 +322,11 @@ export async function updateAuthenticatedUI(data) {
toggle.id = "userDropdownToggle";
toggle.classList.add("btn","btn-user");
toggle.setAttribute("title", t("user_settings"));
- toggle.innerHTML = `${avatarHTML}${data.username}`;
+ toggle.innerHTML = `
+ ${avatarHTML}
+ ${usernameText}
+
+ `;
dd.append(toggle);
// menu
@@ -375,9 +387,13 @@ export async function updateAuthenticatedUI(data) {
});
} else {
- // update avatar only
+ // update avatar & username only
const tog = dd.querySelector("#userDropdownToggle");
- tog.innerHTML = `${avatarHTML}${data.username}`;
+ tog.innerHTML = `
+ ${avatarHTML}
+ ${usernameText}
+
+ `;
dd.style.display = "inline-block";
}
}
diff --git a/public/js/authModals.js b/public/js/authModals.js
index 642ba36..2fc2168 100644
--- a/public/js/authModals.js
+++ b/public/js/authModals.js
@@ -184,105 +184,128 @@ function normalizePicUrl(raw) {
export async function openUserPanel() {
// 1) load data
const { username = 'User', profile_picture = '', totp_enabled = false } = await fetchCurrentUser();
- const raw = profile_picture;
- const picUrl = normalizePicUrl(raw);
+ const raw = profile_picture;
+ const picUrl = normalizePicUrl(raw) || '/assets/default-avatar.png';
// 2) dark‐mode helpers
- const isDark = document.body.classList.contains('dark-mode');
+ const isDark = document.body.classList.contains('dark-mode');
const overlayBg = isDark ? 'rgba(0,0,0,0.7)' : 'rgba(0,0,0,0.3)';
- const contentCss = `
- background: ${isDark ? '#2c2c2c' : '#fff'};
- color: ${isDark ? '#e0e0e0' : '#000'};
- padding: 20px;
- max-width: 600px;
- width: 90%;
- border-radius: 8px;
- overflow-y: auto;
- max-height: 415px;
- border: ${isDark ? '1px solid #444' : '1px solid #ccc'};
- box-sizing: border-box;
+ const contentStyle = `
+ background: ${isDark ? '#2c2c2c' : '#fff'};
+ color: ${isDark ? '#e0e0e0' : '#000'};
+ padding: 20px;
+ max-width: 600px; width:90%;
+ border-radius: 8px;
+ overflow-y: auto; max-height: 415px;
+ border: ${isDark ? '1px solid #444' : '1px solid #ccc'};
+ box-sizing: border-box;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+ `;
- /* hide scrollbar in Firefox */
- scrollbar-width: none;
- /* hide scrollbar in IE 10+ */
- -ms-overflow-style: none;
-`;
-
- // 3) build or re-use modal
+ // 3) create or reuse modal
let modal = document.getElementById('userPanelModal');
if (!modal) {
+ // overlay
modal = document.createElement('div');
modal.id = 'userPanelModal';
- modal.style.cssText = `
- position:fixed; top:0; left:0; right:0; bottom:0;
- background:${overlayBg};
- display:flex; align-items:center; justify-content:center;
- z-index:1000;
+ Object.assign(modal.style, {
+ position: 'fixed',
+ top: '0',
+ left: '0',
+ right: '0',
+ bottom: '0',
+ background: overlayBg,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ zIndex: '1000',
+ });
+
+ // content container
+ const content = document.createElement('div');
+ content.className = 'modal-content';
+ content.style.cssText = contentStyle;
+
+ // close button
+ const closeBtn = document.createElement('span');
+ closeBtn.id = 'closeUserPanel';
+ closeBtn.className = 'editor-close-btn';
+ closeBtn.textContent = '×';
+ closeBtn.addEventListener('click', () => modal.style.display = 'none');
+ content.appendChild(closeBtn);
+
+ // avatar + picker
+ const avatarWrapper = document.createElement('div');
+ avatarWrapper.style.cssText = 'text-align:center; margin-bottom:20px;';
+ const avatarInner = document.createElement('div');
+ avatarInner.style.cssText = 'position:relative; width:80px; height:80px; margin:0 auto;';
+ const img = document.createElement('img');
+ img.id = 'profilePicPreview';
+ img.src = picUrl;
+ img.alt = 'Profile Picture';
+ img.style.cssText = 'width:100%; height:100%; border-radius:50%; object-fit:cover;';
+ avatarInner.appendChild(img);
+ const label = document.createElement('label');
+ label.htmlFor = 'profilePicInput';
+ label.style.cssText = `
+ position:absolute; bottom:0; right:0;
+ width:24px; height:24px;
+ background:rgba(0,0,0,0.6);
+ border-radius:50%; display:flex;
+ align-items:center; justify-content:center;
+ cursor:pointer;
`;
+ const editIcon = document.createElement('i');
+ editIcon.className = 'material-icons';
+ editIcon.style.cssText = 'color:#fff; font-size:16px;';
+ editIcon.textContent = 'edit';
+ label.appendChild(editIcon);
+ avatarInner.appendChild(label);
+ const fileInput = document.createElement('input');
+ fileInput.type = 'file';
+ fileInput.id = 'profilePicInput';
+ fileInput.accept = 'image/*';
+ fileInput.style.display = 'none';
+ avatarInner.appendChild(fileInput);
+ avatarWrapper.appendChild(avatarInner);
+ content.appendChild(avatarWrapper);
- modal.innerHTML = `
-