diff --git a/README.md b/README.md
index cf0aa6f..b5779c8 100644
--- a/README.md
+++ b/README.md
@@ -109,6 +109,7 @@ The following changes are already implemented:
* 🛑 [Add support for Account Suspension (MSC3823)](https://github.com/etkecc/synapse-admin/pull/195)
* 🗑️ [Add "Purge Remote Media" button](https://github.com/etkecc/synapse-admin/pull/237)
* [Respect base url (`BASE_PATH` / `vite build --base`) when loading `config.json`](https://github.com/etkecc/synapse-admin/pull/274)
+* [Add Users' Account Data tab](https://github.com/etkecc/synapse-admin/pull/276)
#### exclusive for [etke.cc](https://etke.cc) customers
diff --git a/src/components/UserAccountData.tsx b/src/components/UserAccountData.tsx
new file mode 100644
index 0000000..28cfec5
--- /dev/null
+++ b/src/components/UserAccountData.tsx
@@ -0,0 +1,65 @@
+import { useDataProvider, useRecordContext, useTranslate } from "react-admin";
+import { useEffect, useState } from "react";
+import { Typography, Box, Stack, Accordion, AccordionSummary, AccordionDetails } from "@mui/material";
+import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
+import { SynapseDataProvider } from "../synapse/dataProvider";
+
+const UserAccountData = () => {
+ const dataProvider = useDataProvider() as SynapseDataProvider;
+ const record = useRecordContext();
+ const translate = useTranslate();
+ const [globalAccountData, setGlobalAccountData] = useState({});
+ const [roomsAccountData, setRoomsAccountData] = useState({});
+
+ if (!record) {
+ return null;
+ }
+
+ useEffect(() => {
+ const fetchAccountData = async () => {
+ const accountData = await dataProvider.getAccountData(record.id);
+ setGlobalAccountData(accountData.account_data.global);
+ setRoomsAccountData(accountData.account_data.rooms);
+ };
+ fetchAccountData();
+ }, []);
+
+ if (Object.keys(globalAccountData).length === 0 && Object.keys(roomsAccountData).length === 0) {
+ return {translate('ra.navigation.no_results', {
+ resource: 'Account Data',
+ _: 'No results found.',
+ })};
+ }
+
+ return <>
+
+ {translate('resources.users.account_data.title')}
+
+
+
+ }>
+ {translate('resources.users.account_data.global')}
+
+
+ {JSON.stringify(globalAccountData, null, 4)}
+
+
+
+ }>
+ {translate('resources.users.account_data.rooms')}
+
+
+ {JSON.stringify(roomsAccountData, null, 4)}
+
+
+
+
+
+ >
+}
+
+export default UserAccountData;
\ No newline at end of file
diff --git a/src/i18n/de.ts b/src/i18n/de.ts
index 1cef0ad..97933b4 100644
--- a/src/i18n/de.ts
+++ b/src/i18n/de.ts
@@ -55,7 +55,7 @@ const de: SynapseTranslationMessages = {
},
users: {
invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.",
- tabs: { sso: "SSO", experimental: "Experimentell", limits: "Rate Limits" },
+ tabs: { sso: "SSO", experimental: "Experimentell", limits: "Rate Limits", account_data: "Kontodaten" },
},
rooms: {
details: "Raumdetails",
@@ -229,6 +229,11 @@ const de: SynapseTranslationMessages = {
messages_per_second_text: "Die Anzahl der Aktionen, die in einer Sekunde durchgeführt werden können.",
burst_count: "Burst-Anzahl",
burst_count_text: "Die Anzahl der Aktionen, die vor der Begrenzung durchgeführt werden können.",
+ },
+ account_data: {
+ title: "Kontodaten",
+ global: "Globale",
+ rooms: "Räume",
}
},
rooms: {
diff --git a/src/i18n/en.ts b/src/i18n/en.ts
index 8d3dab7..de50026 100644
--- a/src/i18n/en.ts
+++ b/src/i18n/en.ts
@@ -29,6 +29,7 @@ const en: SynapseTranslationMessages = {
sso: "SSO",
experimental: "Experimental",
limits: "Rate Limits",
+ account_data: "Account Data",
},
},
rooms: {
@@ -202,6 +203,11 @@ const en: SynapseTranslationMessages = {
messages_per_second_text: "The number of actions that can be performed in a second.",
burst_count: "Burst count",
burst_count_text: "How many actions that can be performed before being limited.",
+ },
+ account_data: {
+ title: "Account Data",
+ global: "Global",
+ rooms: "Rooms",
}
},
rooms: {
diff --git a/src/i18n/fa.ts b/src/i18n/fa.ts
index 676570d..c7f6a7f 100644
--- a/src/i18n/fa.ts
+++ b/src/i18n/fa.ts
@@ -24,7 +24,7 @@ const fa: SynapseTranslationMessages = {
},
users: {
invalid_user_id: "بخش محلی یک شناسه کاربری ماتریکس بدون سرور خانگی.",
- tabs: { sso: "SSO", experimental: "تجربی", limits: "محدودیت ها" },
+ tabs: { sso: "SSO", experimental: "تجربی", limits: "محدودیت ها", account_data: "داده های کاربر" },
},
rooms: {
tabs: {
@@ -195,6 +195,11 @@ const fa: SynapseTranslationMessages = {
messages_per_second_text: "تعداد عملیاتی که می تواند در یک ثانیه انجام شود.",
burst_count: "تعداد پیچیدگی",
burst_count_text: "تعداد عملیاتی که می تواند قبل از محدودیت انجام شود.",
+ },
+ account_data: {
+ title: "داده های کاربر",
+ global: "عمومی",
+ rooms: "اتاق ها",
}
},
rooms: {
diff --git a/src/i18n/fr.ts b/src/i18n/fr.ts
index b2de5d5..2158d87 100644
--- a/src/i18n/fr.ts
+++ b/src/i18n/fr.ts
@@ -24,7 +24,7 @@ const fr: SynapseTranslationMessages = {
},
users: {
invalid_user_id: "Partie locale d'un identifiant utilisateur Matrix sans le nom du serveur d’accueil.",
- tabs: { sso: "Authentification unique", experimental: "Expérimental", limits: "Limites" },
+ tabs: { sso: "Authentification unique", experimental: "Expérimental", limits: "Limites", account_data: "Données du compte" },
},
rooms: {
tabs: {
@@ -197,6 +197,11 @@ const fr: SynapseTranslationMessages = {
messages_per_second_text: "Le nombre d'actions que l'utilisateur peut effectuer par seconde.",
burst_count: "Compteur de pics",
burst_count_text: "Le nombre d'actions que l'utilisateur peut effectuer avant d'être limité.",
+ },
+ account_data: {
+ title: "Données du compte",
+ global: "Globales",
+ rooms: "Salons",
}
},
rooms: {
diff --git a/src/i18n/index.d.ts b/src/i18n/index.d.ts
index 08a7518..4024118 100644
--- a/src/i18n/index.d.ts
+++ b/src/i18n/index.d.ts
@@ -22,7 +22,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
};
users: {
invalid_user_id: string;
- tabs: { sso: string; experimental: string; limits: string; };
+ tabs: { sso: string; experimental: string; limits: string; account_data: string; };
};
rooms: {
details?: string; // TODO: fa, fr, it, zh
@@ -195,6 +195,11 @@ interface SynapseTranslationMessages extends TranslationMessages {
burst_count: string;
burst_count_text: string;
};
+ account_data: {
+ title: string;
+ global: string;
+ rooms: string;
+ }
};
rooms: {
name: string;
diff --git a/src/i18n/it.ts b/src/i18n/it.ts
index 3fc1ec7..204d26a 100644
--- a/src/i18n/it.ts
+++ b/src/i18n/it.ts
@@ -24,7 +24,7 @@ const it: SynapseTranslationMessages = {
},
users: {
invalid_user_id: "ID utente non valido su questo homeserver.",
- tabs: { sso: "SSO", experimental: "Sperimentale", limits: "Limiti" },
+ tabs: { sso: "SSO", experimental: "Sperimentale", limits: "Limiti", account_data: "Dati del profilo" },
},
rooms: {
tabs: {
@@ -195,6 +195,11 @@ const it: SynapseTranslationMessages = {
messages_per_second_text: "Il numero di azioni che l'utente può eseguire al secondo.",
burst_count: "Burst-conteggio",
burst_count_text: "Il numero di azioni che l'utente può eseguire prima di essere limitato.",
+ },
+ account_data: {
+ title: "Dati del profilo",
+ global: "Globale",
+ rooms: "Stanza",
}
},
rooms: {
diff --git a/src/i18n/ru.ts b/src/i18n/ru.ts
index dde66cb..918db16 100644
--- a/src/i18n/ru.ts
+++ b/src/i18n/ru.ts
@@ -50,7 +50,7 @@ const ru: SynapseTranslationMessages = {
},
users: {
invalid_user_id: "Локальная часть ID пользователя Matrix без адреса домашнего сервера.",
- tabs: { sso: "SSO", experimental: "Экспериментальные", limits: "Ограничения" },
+ tabs: { sso: "SSO", experimental: "Экспериментальные", limits: "Ограничения", account_data: "Данные пользователя" },
},
rooms: {
details: "Данные комнаты",
@@ -232,6 +232,11 @@ const ru: SynapseTranslationMessages = {
messages_per_second_text: "Количество действий, которые могут быть выполнены в секунду.",
burst_count: "Burst-счётчик",
burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.",
+ },
+ account_data: {
+ title: "Данные пользователя",
+ global: "Глобальные",
+ rooms: "Комнаты",
}
},
rooms: {
diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts
index 88f89f9..6b3dd94 100644
--- a/src/i18n/zh.ts
+++ b/src/i18n/zh.ts
@@ -52,7 +52,7 @@ const zh: SynapseTranslationMessages = {
},
users: {
invalid_user_id: "必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver",
- tabs: { sso: "SSO", experimental: "实验性", limits: "限制" },
+ tabs: { sso: "SSO", experimental: "实验性", limits: "限制", account_data: "账户数据" },
},
rooms: {
tabs: {
@@ -221,6 +221,11 @@ const zh: SynapseTranslationMessages = {
messages_per_second_text: "每秒可以执行的操作数。",
burst_count: "Burst-计数",
burst_count_text: "在限制之前可以执行的操作数。",
+ },
+ account_data: {
+ title: "账户数据",
+ global: "全局",
+ rooms: "房间",
}
},
rooms: {
diff --git a/src/resources/users.tsx b/src/resources/users.tsx
index bbf2851..e04be10 100644
--- a/src/resources/users.tsx
+++ b/src/resources/users.tsx
@@ -10,6 +10,7 @@ import SettingsInputComponentIcon from "@mui/icons-material/SettingsInputCompone
import ScienceIcon from "@mui/icons-material/Science";
import LockClockIcon from '@mui/icons-material/LockClock';
import ViewListIcon from "@mui/icons-material/ViewList";
+import DocumentScannerIcon from "@mui/icons-material/DocumentScanner";
import { useEffect, useState } from "react";
import { Alert, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
@@ -81,6 +82,7 @@ import ExperimentalFeaturesList from "../components/ExperimentalFeatures";
import UserRateLimits from "../components/UserRateLimits";
import { User, UsernameAvailabilityResult } from "../synapse/dataProvider";
import { MakeAdminBtn } from "./rooms";
+import UserAccountData from "../components/UserAccountData";
const choices_medium = [
{ id: "email", name: "resources.users.email" },
@@ -556,6 +558,10 @@ export const UserEdit = (props: EditProps) => {
} path="limits">
+
+ } path="accountdata">
+
+
);
diff --git a/src/synapse/dataProvider.ts b/src/synapse/dataProvider.ts
index cd51e65..5cade98 100644
--- a/src/synapse/dataProvider.ts
+++ b/src/synapse/dataProvider.ts
@@ -260,6 +260,17 @@ export interface RateLimitsModel {
burst_count?: number;
}
+export interface AccountDataModel {
+ account_data: {
+ global: {
+ [key: string]: object;
+ },
+ rooms: {
+ [key: string]: object;
+ };
+ }
+}
+
export interface UsernameAvailabilityResult {
available?: boolean;
error?: string;
@@ -309,6 +320,7 @@ export interface SynapseDataProvider extends DataProvider {
updateFeatures: (id: Identifier, features: ExperimentalFeaturesModel) => Promise;
getRateLimits: (id: Identifier) => Promise;
setRateLimits: (id: Identifier, rateLimits: RateLimitsModel) => Promise;
+ getAccountData: (id: Identifier) => Promise;
checkUsernameAvailability: (username: string) => Promise;
makeRoomAdmin: (room_id: string, user_id: string) => Promise<{ success: boolean; error?: string; errcode?: string }>;
getServerRunningProcess: (etkeAdminUrl: string) => Promise;
@@ -911,6 +923,12 @@ const baseDataProvider: SynapseDataProvider = {
const { json } = await jsonClient(endpoint_url);
return json as RateLimitsModel;
},
+ getAccountData: async (id: Identifier) => {
+ const base_url = localStorage.getItem("base_url");
+ const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/accountdata`;
+ const { json } = await jsonClient(endpoint_url);
+ return json as AccountDataModel;
+ },
setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => {
const filtered = Object.entries(rateLimits).
filter(([key, value]) => value !== null && value !== undefined).