diff --git a/static/js/core/types.js b/static/js/core/types.js
index ef006901..5092ff06 100644
--- a/static/js/core/types.js
+++ b/static/js/core/types.js
@@ -149,7 +149,7 @@
*
* @typedef {Object} User
* @property {string} id
- * @property {string} username
+ * @property {string} [username] Optional handle (PR 16); claim-once via /api/auth/me/profile (PR 24). Omitted from JSON when null.
* @property {string} email
* @property {string} role
* @property {number} storage_quota_bytes
@@ -162,8 +162,9 @@
* @property {string|null} [image] Avatar URL or data URI
* @property {boolean} can_edit_image False for OIDC-only users
* @property {boolean} is_external True for magic-link / OIDC-only / OCM recipients
- * @property {string} [given_name] OIDC `given_name` claim, when set
- * @property {string} [family_name] OIDC `family_name` claim, when set
+ * @property {string} [given_name] First/given name; set at OIDC JIT or via PATCH /api/auth/me/profile (PR 24)
+ * @property {string} [family_name] Last/family name; set at OIDC JIT or via PATCH /api/auth/me/profile (PR 24)
+ * @property {string} [email_verified_at] ISO 8601 timestamp of the first proof-of-email-control (PR 23). Omitted when unverified.
*/
/**
diff --git a/static/js/views/profile/profile.js b/static/js/views/profile/profile.js
index 28fe3796..312dc51e 100644
--- a/static/js/views/profile/profile.js
+++ b/static/js/views/profile/profile.js
@@ -336,6 +336,8 @@ async function init() {
document.getElementById('password-section').classList.add('hidden');
}
+ _renderProfileEdit(user);
+
loadAppPasswords();
try {
@@ -597,6 +599,137 @@ async function revokeAppPassword(id, label) {
}
}
+/**
+ * Render the Edit Profile card based on the current user.
+ *
+ * For OIDC users: the entire form is hidden and a single alert tells
+ * them their profile is managed at the IdP. For local users: the form
+ * is populated from the current values, and the username input is
+ * disabled when a handle is already claimed (PR 24's claim-once
+ * policy).
+ *
+ * @param {import('../../core/types.js').User} user
+ */
+function _renderProfileEdit(user) {
+ const oidcNote = document.getElementById('profile-edit-oidc-note');
+ const form = document.getElementById('profile-edit-form');
+ if (!oidcNote || !form) return;
+
+ const isOidc = user.auth_provider && user.auth_provider !== 'local';
+ if (isOidc) {
+ oidcNote.classList.remove('hidden');
+ form.classList.add('hidden');
+ return;
+ }
+
+ oidcNote.classList.add('hidden');
+ form.classList.remove('hidden');
+
+ const usernameInput = /** @type {HTMLInputElement} */ (document.getElementById('profile-edit-username'));
+ const usernameHint = document.getElementById('profile-edit-username-hint');
+ const givenInput = /** @type {HTMLInputElement} */ (document.getElementById('profile-edit-given-name'));
+ const familyInput = /** @type {HTMLInputElement} */ (document.getElementById('profile-edit-family-name'));
+
+ if (user.username) {
+ usernameInput.value = user.username;
+ usernameInput.disabled = true;
+ if (usernameHint) {
+ usernameHint.textContent = i18n.t('profile.username_already_claimed');
+ }
+ } else {
+ usernameInput.value = '';
+ usernameInput.disabled = false;
+ if (usernameHint) {
+ usernameHint.textContent = i18n.t('profile.username_claim_hint');
+ }
+ }
+ givenInput.value = user.given_name || '';
+ familyInput.value = user.family_name || '';
+}
+
+/**
+ * Submit the profile edit form. Only sends fields the user can change:
+ * - Username only if not already claimed (input wasn't disabled).
+ * - Given/family names only when their value differs from the
+ * current (avoids 400-rejecting an empty string the user never
+ * touched).
+ *
+ * @param {Event} e
+ */
+async function submitProfile(e) {
+ e.preventDefault();
+
+ const statusEl = document.getElementById('profile-edit-status');
+ const btn = /** @type {HTMLButtonElement} */ (document.getElementById('profile-edit-submit'));
+ const usernameInput = /** @type {HTMLInputElement} */ (document.getElementById('profile-edit-username'));
+ const givenInput = /** @type {HTMLInputElement} */ (document.getElementById('profile-edit-given-name'));
+ const familyInput = /** @type {HTMLInputElement} */ (document.getElementById('profile-edit-family-name'));
+
+ /** @type {{ username?: string, given_name?: string, family_name?: string }} */
+ const body = {};
+ if (!usernameInput.disabled && usernameInput.value.trim()) {
+ body.username = usernameInput.value.trim();
+ }
+ const given = givenInput.value.trim();
+ if (given) body.given_name = given;
+ const family = familyInput.value.trim();
+ if (family) body.family_name = family;
+
+ if (Object.keys(body).length === 0) {
+ statusEl.innerHTML = `
${escapeHtml(i18n.t('profile.profile_no_changes'))}
`;
+ return false;
+ }
+
+ btn.disabled = true;
+ btn.innerHTML = ` ${escapeHtml(i18n.t('profile.updating'))}`;
+
+ try {
+ const resp = await fetch(`${API}/auth/me/profile`, {
+ method: 'PATCH',
+ headers: headers(),
+ credentials: 'same-origin',
+ body: JSON.stringify(body)
+ });
+
+ if (resp.ok) {
+ /** @type {import('../../core/types.js').User} */
+ const updated = await resp.json();
+ _renderProfileEdit(updated);
+ // Also refresh the read-only "Account Details" username field.
+ const detailUsername = document.getElementById('p-detail-username');
+ if (detailUsername) detailUsername.textContent = updated.username || '—';
+ const topUsername = document.getElementById('p-username');
+ if (topUsername && updated.username) topUsername.textContent = updated.username;
+ statusEl.innerHTML = ` ${escapeHtml(i18n.t('profile.profile_saved'))}
`;
+ } else if (resp.status === 409) {
+ const err = await resp.json().catch(() => ({}));
+ // Distinguish "username taken" from "username immutable" using the
+ // human-readable message — both are 409. Server audit has the
+ // structured `reason` field; the JSON body just carries `message`.
+ const msg = (err.message || '').toLowerCase();
+ const key = msg.includes('already claimed') ? 'profile.username_immutable_error' : 'profile.username_taken_error';
+ statusEl.innerHTML = ` ${escapeHtml(i18n.t(key))}
`;
+ } else if (resp.status === 403) {
+ statusEl.innerHTML = ` ${escapeHtml(i18n.t('profile.edit_oidc_managed'))}
`;
+ } else {
+ const err = await resp.json().catch(() => ({}));
+ statusEl.innerHTML =
+ ' ' +
+ escapeHtml(err.message || i18n.t('profile.profile_save_failed')) +
+ '
';
+ }
+ } catch (err) {
+ statusEl.innerHTML =
+ ' ' +
+ escapeHtml(i18n.t('profile.error_network', { message: /** @type {Error} */ (err).message })) +
+ '
';
+ }
+
+ btn.disabled = false;
+ btn.innerHTML = ` ${escapeHtml(i18n.t('profile.save_profile'))}`;
+ return false;
+}
+
/** @param {string} str */
function escapeHtml(str) {
var div = document.createElement('div');
@@ -608,6 +741,7 @@ init();
/* Wire up event handlers (replaces inline onclick/onsubmit) */
document.getElementById('password-form').addEventListener('submit', changePassword);
+document.getElementById('profile-edit-form')?.addEventListener('submit', submitProfile);
document.getElementById('app-pw-generate').addEventListener('click', createAppPassword);
document.getElementById('app-pw-copy-btn').addEventListener('click', copyAppPassword);
document.getElementById('app-pw-auto-toggle').addEventListener('click', toggleAutoPasswords);
diff --git a/static/locales/ar.json b/static/locales/ar.json
index c3b7dc09..b08f05b4 100644
--- a/static/locales/ar.json
+++ b/static/locales/ar.json
@@ -703,6 +703,18 @@
"minutes_ago": "منذ {{n}} دقيقة",
"hours_ago": "منذ {{n}} ساعة",
"days_ago": "منذ {{n}} يوم",
+ "edit_profile": "تعديل الملف الشخصي",
+ "edit_oidc_managed": "لتغيير معلوماتك (الاسم، الاسم الأول، صورة الملف الشخصي، …)، يرجى تحديثها لدى مزود الهوية. ستظهر تغييراتك عند تسجيل الدخول التالي.",
+ "username_claim_hint": "2-64 حرفًا، أحرف / أرقام / نقطة / شرطة / شرطة سفلية. بمجرد الاختيار، لا يمكن تغيير اسم المستخدم (عملاء DAV/NextCloud يعتمدون عليه).",
+ "username_already_claimed": "اسم المستخدم محدد ولا يمكن تغييره (عملاء DAV/NextCloud يعتمدون عليه).",
+ "given_name": "الاسم الأول",
+ "family_name": "اسم العائلة",
+ "save_profile": "حفظ التغييرات",
+ "profile_saved": "تم تحديث الملف الشخصي",
+ "profile_no_changes": "لا توجد تغييرات لحفظها.",
+ "profile_save_failed": "فشل الحفظ",
+ "username_taken_error": "اسم المستخدم هذا مستخدم بالفعل.",
+ "username_immutable_error": "اسم المستخدم الخاص بك محدد بالفعل ولا يمكن تغييره هنا. اتصل بالمسؤول إذا كنت بحاجة إلى إعادة التسمية.",
"change_password": "تغيير كلمة المرور",
"current_password": "كلمة المرور الحالية",
"new_password": "كلمة المرور الجديدة",
diff --git a/static/locales/de.json b/static/locales/de.json
index f7c16a7a..8b438032 100644
--- a/static/locales/de.json
+++ b/static/locales/de.json
@@ -703,6 +703,18 @@
"minutes_ago": "vor {{n}} Min",
"hours_ago": "vor {{n}} Std",
"days_ago": "vor {{n}} Tagen",
+ "edit_profile": "Profil bearbeiten",
+ "edit_oidc_managed": "Um Ihre Informationen (Name, Vorname, Profilbild, …) zu ändern, aktualisieren Sie sie bitte bei Ihrem Identity-Provider. Ihre Änderungen erscheinen bei der nächsten Anmeldung.",
+ "username_claim_hint": "2–64 Zeichen, Buchstaben / Ziffern / Punkt / Bindestrich / Unterstrich. Nach der Wahl kann der Benutzername nicht mehr geändert werden (DAV/NextCloud-Clients hängen davon ab).",
+ "username_already_claimed": "Benutzername ist gesetzt und kann nicht geändert werden (DAV/NextCloud-Clients hängen davon ab).",
+ "given_name": "Vorname",
+ "family_name": "Nachname",
+ "save_profile": "Änderungen speichern",
+ "profile_saved": "Profil aktualisiert",
+ "profile_no_changes": "Keine Änderungen zu speichern.",
+ "profile_save_failed": "Speichern fehlgeschlagen",
+ "username_taken_error": "Dieser Benutzername ist bereits vergeben.",
+ "username_immutable_error": "Ihr Benutzername ist bereits gesetzt und kann hier nicht geändert werden. Wenden Sie sich an einen Administrator, wenn Sie umbenennen möchten.",
"change_password": "Passwort ändern",
"current_password": "Aktuelles Passwort",
"new_password": "Neues Passwort",
diff --git a/static/locales/en.json b/static/locales/en.json
index 2754eb01..0010c459 100644
--- a/static/locales/en.json
+++ b/static/locales/en.json
@@ -720,6 +720,18 @@
"minutes_ago": "{{n}} min ago",
"hours_ago": "{{n}}h ago",
"days_ago": "{{n}} days ago",
+ "edit_profile": "Edit Profile",
+ "edit_oidc_managed": "To change your information (name, first name, profile picture, …), please update it at your identity provider. Your changes will appear on your next sign-in.",
+ "username_claim_hint": "2–64 characters, letters/digits/dot/dash/underscore. Once chosen, the username can't be changed (DAV/NextCloud clients depend on it).",
+ "username_already_claimed": "Username is set and can't be changed (DAV/NextCloud clients depend on it).",
+ "given_name": "First name",
+ "family_name": "Last name",
+ "save_profile": "Save changes",
+ "profile_saved": "Profile updated",
+ "profile_no_changes": "No changes to save.",
+ "profile_save_failed": "Save failed",
+ "username_taken_error": "That username is already taken.",
+ "username_immutable_error": "Your username is already set and can't be changed here. Contact an administrator if you need a rename.",
"change_password": "Change Password",
"current_password": "Current Password",
"new_password": "New Password",
diff --git a/static/locales/es.json b/static/locales/es.json
index a9ea765a..df412c6e 100644
--- a/static/locales/es.json
+++ b/static/locales/es.json
@@ -703,6 +703,18 @@
"minutes_ago": "hace {{n}} min",
"hours_ago": "hace {{n}}h",
"days_ago": "hace {{n}} días",
+ "edit_profile": "Editar perfil",
+ "edit_oidc_managed": "Para cambiar tu información (nombre, apellidos, foto de perfil, …), actualízala en tu proveedor de identidad. Los cambios se aplicarán en tu próximo inicio de sesión.",
+ "username_claim_hint": "Entre 2 y 64 caracteres, letras / dígitos / punto / guion / subrayado. Una vez elegido, el nombre de usuario no se puede cambiar (los clientes DAV/NextCloud dependen de él).",
+ "username_already_claimed": "Nombre de usuario fijado y no modificable (los clientes DAV/NextCloud dependen de él).",
+ "given_name": "Nombre",
+ "family_name": "Apellidos",
+ "save_profile": "Guardar cambios",
+ "profile_saved": "Perfil actualizado",
+ "profile_no_changes": "Sin cambios que guardar.",
+ "profile_save_failed": "Error al guardar",
+ "username_taken_error": "Ese nombre de usuario ya está en uso.",
+ "username_immutable_error": "Tu nombre de usuario ya está fijado y no se puede cambiar aquí. Contacta con un administrador si necesitas renombrarlo.",
"change_password": "Cambiar Contraseña",
"current_password": "Contraseña Actual",
"new_password": "Nueva Contraseña",
diff --git a/static/locales/fa.json b/static/locales/fa.json
index bb640cfb..3936e963 100644
--- a/static/locales/fa.json
+++ b/static/locales/fa.json
@@ -686,6 +686,18 @@
"minutes_ago": "{{n}} دقیقه پیش",
"hours_ago": "{{n}} ساعت پیش",
"days_ago": "{{n}} روز پیش",
+ "edit_profile": "ویرایش نمایه",
+ "edit_oidc_managed": "برای تغییر اطلاعات خود (نام، نام خانوادگی، عکس نمایه، …)، لطفاً آنها را در ارائهدهنده هویت خود بهروز کنید. تغییرات شما در ورود بعدی ظاهر خواهد شد.",
+ "username_claim_hint": "۲ تا ۶۴ کاراکتر، حروف / ارقام / نقطه / خط تیره / زیرخط. پس از انتخاب، نام کاربری قابل تغییر نیست (کلاینتهای DAV/NextCloud به آن وابستهاند).",
+ "username_already_claimed": "نام کاربری تنظیم شده و قابل تغییر نیست (کلاینتهای DAV/NextCloud به آن وابستهاند).",
+ "given_name": "نام",
+ "family_name": "نام خانوادگی",
+ "save_profile": "ذخیره تغییرات",
+ "profile_saved": "نمایه بهروز شد",
+ "profile_no_changes": "تغییری برای ذخیره وجود ندارد.",
+ "profile_save_failed": "ذخیره ناموفق بود",
+ "username_taken_error": "این نام کاربری قبلاً گرفته شده است.",
+ "username_immutable_error": "نام کاربری شما قبلاً تنظیم شده و در اینجا قابل تغییر نیست. در صورت نیاز به تغییر نام، با مدیر تماس بگیرید.",
"change_password": "تغییر رمز عبور",
"current_password": "رمز فعلی",
"new_password": "رمز جدید",
diff --git a/static/locales/fr.json b/static/locales/fr.json
index 23e552b6..010b5054 100644
--- a/static/locales/fr.json
+++ b/static/locales/fr.json
@@ -720,6 +720,18 @@
"minutes_ago": "il y a {{n}} min",
"hours_ago": "il y a {{n}}h",
"days_ago": "il y a {{n}} jours",
+ "edit_profile": "Modifier le profil",
+ "edit_oidc_managed": "Pour modifier vos informations (nom, prénom, photo de profil, …), veuillez les mettre à jour chez votre fournisseur d'identité. Vos changements apparaîtront à votre prochaine connexion.",
+ "username_claim_hint": "2 à 64 caractères, lettres / chiffres / point / tiret / souligné. Une fois choisi, le nom d'utilisateur ne peut plus être modifié (les clients DAV/NextCloud en dépendent).",
+ "username_already_claimed": "Nom d'utilisateur fixé et non modifiable (les clients DAV/NextCloud en dépendent).",
+ "given_name": "Prénom",
+ "family_name": "Nom",
+ "save_profile": "Enregistrer",
+ "profile_saved": "Profil mis à jour",
+ "profile_no_changes": "Aucun changement à enregistrer.",
+ "profile_save_failed": "Échec de l'enregistrement",
+ "username_taken_error": "Ce nom d'utilisateur est déjà pris.",
+ "username_immutable_error": "Votre nom d'utilisateur est déjà défini et ne peut plus être modifié ici. Contactez un administrateur si vous souhaitez le renommer.",
"change_password": "Changer le mot de passe",
"current_password": "Mot de passe actuel",
"new_password": "Nouveau mot de passe",
diff --git a/static/locales/hi.json b/static/locales/hi.json
index 546e3666..9801d852 100644
--- a/static/locales/hi.json
+++ b/static/locales/hi.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}} मिनट पहले",
"hours_ago": "{{n}} घंटे पहले",
"days_ago": "{{n}} दिन पहले",
+ "edit_profile": "प्रोफ़ाइल संपादित करें",
+ "edit_oidc_managed": "अपनी जानकारी (नाम, प्रथम नाम, प्रोफ़ाइल चित्र, …) बदलने के लिए, कृपया अपने पहचान प्रदाता पर इसे अद्यतन करें। आपके परिवर्तन अगले साइन-इन पर दिखाई देंगे।",
+ "username_claim_hint": "2–64 अक्षर, अक्षर / अंक / डॉट / डैश / अंडरस्कोर। एक बार चुनने के बाद, उपयोगकर्ता नाम नहीं बदला जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।",
+ "username_already_claimed": "उपयोगकर्ता नाम सेट है और बदला नहीं जा सकता (DAV/NextCloud क्लाइंट इस पर निर्भर करते हैं)।",
+ "given_name": "प्रथम नाम",
+ "family_name": "अंतिम नाम",
+ "save_profile": "परिवर्तन सहेजें",
+ "profile_saved": "प्रोफ़ाइल अद्यतन की गई",
+ "profile_no_changes": "सहेजने के लिए कोई परिवर्तन नहीं।",
+ "profile_save_failed": "सहेजना विफल",
+ "username_taken_error": "यह उपयोगकर्ता नाम पहले से उपयोग में है।",
+ "username_immutable_error": "आपका उपयोगकर्ता नाम पहले से सेट है और यहाँ नहीं बदला जा सकता। यदि आपको नाम बदलने की आवश्यकता है तो किसी व्यवस्थापक से संपर्क करें।",
"change_password": "पासवर्ड बदलें",
"current_password": "वर्तमान पासवर्ड",
"new_password": "नया पासवर्ड",
diff --git a/static/locales/it.json b/static/locales/it.json
index 7e06c09d..35e67951 100644
--- a/static/locales/it.json
+++ b/static/locales/it.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}} min fa",
"hours_ago": "{{n}}h fa",
"days_ago": "{{n}} giorni fa",
+ "edit_profile": "Modifica profilo",
+ "edit_oidc_managed": "Per modificare le tue informazioni (nome, cognome, foto profilo, …), aggiornale presso il tuo identity provider. Le modifiche compariranno al prossimo accesso.",
+ "username_claim_hint": "Da 2 a 64 caratteri, lettere / cifre / punto / trattino / sottolineatura. Una volta scelto, il nome utente non può essere modificato (i client DAV/NextCloud dipendono da esso).",
+ "username_already_claimed": "Nome utente impostato e non modificabile (i client DAV/NextCloud dipendono da esso).",
+ "given_name": "Nome",
+ "family_name": "Cognome",
+ "save_profile": "Salva modifiche",
+ "profile_saved": "Profilo aggiornato",
+ "profile_no_changes": "Nessuna modifica da salvare.",
+ "profile_save_failed": "Salvataggio non riuscito",
+ "username_taken_error": "Questo nome utente è già in uso.",
+ "username_immutable_error": "Il tuo nome utente è già impostato e non può essere cambiato qui. Contatta un amministratore se desideri rinominarlo.",
"change_password": "Cambia Password",
"current_password": "Password Attuale",
"new_password": "Nuova Password",
diff --git a/static/locales/ja.json b/static/locales/ja.json
index cec41168..dc04aa61 100644
--- a/static/locales/ja.json
+++ b/static/locales/ja.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}}分前",
"hours_ago": "{{n}}時間前",
"days_ago": "{{n}}日前",
+ "edit_profile": "プロフィールを編集",
+ "edit_oidc_managed": "情報(姓、名、プロフィール写真など)を変更するには、IDプロバイダーで更新してください。次回サインイン時に反映されます。",
+ "username_claim_hint": "2〜64文字、英数字 / ドット / ハイフン / アンダースコア。一度選択すると、ユーザー名は変更できません(DAV/NextCloudクライアントが依存します)。",
+ "username_already_claimed": "ユーザー名は設定済みで変更できません(DAV/NextCloudクライアントが依存します)。",
+ "given_name": "名",
+ "family_name": "姓",
+ "save_profile": "変更を保存",
+ "profile_saved": "プロフィールを更新しました",
+ "profile_no_changes": "保存する変更はありません。",
+ "profile_save_failed": "保存に失敗しました",
+ "username_taken_error": "このユーザー名はすでに使用されています。",
+ "username_immutable_error": "ユーザー名はすでに設定されており、ここでは変更できません。名前を変更したい場合は管理者にお問い合わせください。",
"change_password": "パスワード変更",
"current_password": "現在のパスワード",
"new_password": "新しいパスワード",
diff --git a/static/locales/ko.json b/static/locales/ko.json
index f6b5250a..9a316b6b 100644
--- a/static/locales/ko.json
+++ b/static/locales/ko.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}}분 전",
"hours_ago": "{{n}}시간 전",
"days_ago": "{{n}}일 전",
+ "edit_profile": "프로필 편집",
+ "edit_oidc_managed": "정보(이름, 성, 프로필 사진 등)를 변경하려면 ID 공급자에서 업데이트하세요. 변경 사항은 다음 로그인 시 반영됩니다.",
+ "username_claim_hint": "2~64자, 영문자 / 숫자 / 점 / 하이픈 / 밑줄. 선택한 후에는 사용자 이름을 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).",
+ "username_already_claimed": "사용자 이름이 설정되어 있어 변경할 수 없습니다(DAV/NextCloud 클라이언트가 이에 의존합니다).",
+ "given_name": "이름",
+ "family_name": "성",
+ "save_profile": "변경 사항 저장",
+ "profile_saved": "프로필이 업데이트되었습니다",
+ "profile_no_changes": "저장할 변경 사항이 없습니다.",
+ "profile_save_failed": "저장 실패",
+ "username_taken_error": "이미 사용 중인 사용자 이름입니다.",
+ "username_immutable_error": "사용자 이름이 이미 설정되어 있어 여기서 변경할 수 없습니다. 이름을 변경하려면 관리자에게 문의하세요.",
"change_password": "비밀번호 변경",
"current_password": "현재 비밀번호",
"new_password": "새 비밀번호",
diff --git a/static/locales/nl.json b/static/locales/nl.json
index 1926d174..ea05f347 100644
--- a/static/locales/nl.json
+++ b/static/locales/nl.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}} min geleden",
"hours_ago": "{{n}}u geleden",
"days_ago": "{{n}} dagen geleden",
+ "edit_profile": "Profiel bewerken",
+ "edit_oidc_managed": "Om uw gegevens (naam, voornaam, profielfoto, …) te wijzigen, werk ze bij bij uw identity provider. De wijzigingen verschijnen bij uw volgende aanmelding.",
+ "username_claim_hint": "2–64 tekens, letters / cijfers / punt / streepje / underscore. Eenmaal gekozen kan de gebruikersnaam niet meer worden gewijzigd (DAV/NextCloud-clients zijn ervan afhankelijk).",
+ "username_already_claimed": "Gebruikersnaam ingesteld en niet wijzigbaar (DAV/NextCloud-clients zijn ervan afhankelijk).",
+ "given_name": "Voornaam",
+ "family_name": "Achternaam",
+ "save_profile": "Wijzigingen opslaan",
+ "profile_saved": "Profiel bijgewerkt",
+ "profile_no_changes": "Geen wijzigingen om op te slaan.",
+ "profile_save_failed": "Opslaan mislukt",
+ "username_taken_error": "Die gebruikersnaam is al in gebruik.",
+ "username_immutable_error": "Uw gebruikersnaam is al ingesteld en kan hier niet worden gewijzigd. Neem contact op met een beheerder als u wilt hernoemen.",
"change_password": "Wachtwoord wijzigen",
"current_password": "Huidig wachtwoord",
"new_password": "Nieuw wachtwoord",
diff --git a/static/locales/pl.json b/static/locales/pl.json
index e9164225..9306477b 100644
--- a/static/locales/pl.json
+++ b/static/locales/pl.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}} min temu",
"hours_ago": "{{n}} godz. temu",
"days_ago": "{{n}} dni temu",
+ "edit_profile": "Edytuj profil",
+ "edit_oidc_managed": "Aby zmienić swoje dane (nazwisko, imię, zdjęcie profilowe, …), zaktualizuj je u swojego dostawcy tożsamości. Twoje zmiany pojawią się przy następnym logowaniu.",
+ "username_claim_hint": "2–64 znaki, litery / cyfry / kropka / myślnik / podkreślenie. Po wybraniu nazwy użytkownika nie można jej zmienić (klienty DAV/NextCloud są od niej zależne).",
+ "username_already_claimed": "Nazwa użytkownika jest ustawiona i nie może być zmieniona (klienty DAV/NextCloud są od niej zależne).",
+ "given_name": "Imię",
+ "family_name": "Nazwisko",
+ "save_profile": "Zapisz zmiany",
+ "profile_saved": "Profil zaktualizowany",
+ "profile_no_changes": "Brak zmian do zapisania.",
+ "profile_save_failed": "Zapis nie powiódł się",
+ "username_taken_error": "Ta nazwa użytkownika jest już zajęta.",
+ "username_immutable_error": "Twoja nazwa użytkownika jest już ustawiona i nie można jej tutaj zmienić. Skontaktuj się z administratorem, jeśli chcesz ją zmienić.",
"change_password": "Zmień hasło",
"current_password": "Bieżące hasło",
"new_password": "Nowe hasło",
diff --git a/static/locales/pt.json b/static/locales/pt.json
index db3175bb..0fb50174 100644
--- a/static/locales/pt.json
+++ b/static/locales/pt.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}} min atrás",
"hours_ago": "{{n}}h atrás",
"days_ago": "{{n}} dias atrás",
+ "edit_profile": "Editar perfil",
+ "edit_oidc_managed": "Para alterar suas informações (nome, sobrenome, foto de perfil, …), atualize-as no seu provedor de identidade. As mudanças aparecerão no próximo login.",
+ "username_claim_hint": "De 2 a 64 caracteres, letras / dígitos / ponto / hífen / sublinhado. Uma vez escolhido, o nome de usuário não pode ser alterado (clientes DAV/NextCloud dependem dele).",
+ "username_already_claimed": "Nome de usuário definido e não pode ser alterado (clientes DAV/NextCloud dependem dele).",
+ "given_name": "Nome",
+ "family_name": "Sobrenome",
+ "save_profile": "Salvar alterações",
+ "profile_saved": "Perfil atualizado",
+ "profile_no_changes": "Sem alterações para salvar.",
+ "profile_save_failed": "Falha ao salvar",
+ "username_taken_error": "Este nome de usuário já está em uso.",
+ "username_immutable_error": "Seu nome de usuário já está definido e não pode ser alterado aqui. Contate um administrador se desejar renomeá-lo.",
"change_password": "Alterar Senha",
"current_password": "Senha Atual",
"new_password": "Nova Senha",
diff --git a/static/locales/ru.json b/static/locales/ru.json
index cabd5af2..7c45a5d4 100644
--- a/static/locales/ru.json
+++ b/static/locales/ru.json
@@ -703,6 +703,18 @@
"minutes_ago": "{{n}} мин назад",
"hours_ago": "{{n}} ч назад",
"days_ago": "{{n}} дн назад",
+ "edit_profile": "Редактировать профиль",
+ "edit_oidc_managed": "Чтобы изменить ваши данные (имя, фамилию, фотографию профиля, …), обновите их у вашего провайдера идентификации. Изменения появятся при следующем входе.",
+ "username_claim_hint": "2–64 символа, буквы / цифры / точка / дефис / подчёркивание. После выбора имя пользователя нельзя изменить (клиенты DAV/NextCloud зависят от него).",
+ "username_already_claimed": "Имя пользователя установлено и не может быть изменено (клиенты DAV/NextCloud зависят от него).",
+ "given_name": "Имя",
+ "family_name": "Фамилия",
+ "save_profile": "Сохранить изменения",
+ "profile_saved": "Профиль обновлён",
+ "profile_no_changes": "Нет изменений для сохранения.",
+ "profile_save_failed": "Не удалось сохранить",
+ "username_taken_error": "Это имя пользователя уже занято.",
+ "username_immutable_error": "Ваше имя пользователя уже установлено и не может быть изменено здесь. Свяжитесь с администратором, если нужно переименовать.",
"change_password": "Изменить пароль",
"current_password": "Текущий пароль",
"new_password": "Новый пароль",
diff --git a/static/locales/zh-TW.json b/static/locales/zh-TW.json
index efe99260..997840ca 100644
--- a/static/locales/zh-TW.json
+++ b/static/locales/zh-TW.json
@@ -686,6 +686,18 @@
"minutes_ago": "{{n}}分鐘前",
"hours_ago": "{{n}}小時前",
"days_ago": "{{n}}天前",
+ "edit_profile": "編輯個人資料",
+ "edit_oidc_managed": "要更改您的資訊(姓名、名字、頭像等),請前往您的身分提供者更新。變更將在您下次登入時顯示。",
+ "username_claim_hint": "2-64 個字元,字母 / 數字 / 點 / 短橫線 / 底線。一旦選定,使用者名稱將無法更改(DAV/NextCloud 用戶端依賴它)。",
+ "username_already_claimed": "使用者名稱已設定,不可更改(DAV/NextCloud 用戶端依賴它)。",
+ "given_name": "名",
+ "family_name": "姓",
+ "save_profile": "儲存變更",
+ "profile_saved": "個人資料已更新",
+ "profile_no_changes": "沒有變更可儲存。",
+ "profile_save_failed": "儲存失敗",
+ "username_taken_error": "該使用者名稱已被使用。",
+ "username_immutable_error": "您的使用者名稱已設定,無法在此更改。如需重新命名,請聯絡管理員。",
"change_password": "修改密碼",
"current_password": "當前密碼",
"new_password": "新密碼",
diff --git a/static/locales/zh.json b/static/locales/zh.json
index ce67e137..af277cd0 100644
--- a/static/locales/zh.json
+++ b/static/locales/zh.json
@@ -686,6 +686,18 @@
"minutes_ago": "{{n}}分钟前",
"hours_ago": "{{n}}小时前",
"days_ago": "{{n}}天前",
+ "edit_profile": "编辑个人资料",
+ "edit_oidc_managed": "要更改您的信息(姓名、名字、头像等),请前往您的身份提供商更新。变更将在您下次登录时显示。",
+ "username_claim_hint": "2-64 个字符,字母 / 数字 / 点 / 短横线 / 下划线。一旦选定,用户名将无法更改(DAV/NextCloud 客户端依赖它)。",
+ "username_already_claimed": "用户名已设置,不可更改(DAV/NextCloud 客户端依赖它)。",
+ "given_name": "名",
+ "family_name": "姓",
+ "save_profile": "保存更改",
+ "profile_saved": "个人资料已更新",
+ "profile_no_changes": "无更改可保存。",
+ "profile_save_failed": "保存失败",
+ "username_taken_error": "该用户名已被占用。",
+ "username_immutable_error": "您的用户名已设置,无法在此更改。如需重命名,请联系管理员。",
"change_password": "修改密码",
"current_password": "当前密码",
"new_password": "新密码",
diff --git a/static/profile.html b/static/profile.html
index 7a984390..0c3ccdf6 100644
--- a/static/profile.html
+++ b/static/profile.html
@@ -116,6 +116,36 @@
+
+
Edit Profile
+
+
+
+ To change your information (name, first name, profile picture, …), please update it at your identity provider. Your changes will appear on your next sign-in.
+
+
+
+
+
Storage
diff --git a/static/sw.js b/static/sw.js
index 99840ea2..74befcaf 100644
--- a/static/sw.js
+++ b/static/sw.js
@@ -1,6 +1,6 @@
// OxiCloud Service Worker
// FIXME: generate cache name according build ?
-const CACHE_NAME = 'oxicloud-cache-v24';
+const CACHE_NAME = 'oxicloud-cache-v25';
// Only cache static assets — NOT HTML files.
// HTML files are served network-first so browsers always get the latest