Compare commits
47 Commits
fix_docker_cd
...
0.9.0
| Author | SHA1 | Date | |
|---|---|---|---|
| ba4345be1e | |||
| b70ee7c55d | |||
| 9f03ec9b0f | |||
| d8d393cdf6 | |||
| 58e02d6dff | |||
| 17379a7325 | |||
| 13fbc419c2 | |||
| e757876c35 | |||
| 8b99695e60 | |||
| 4ab5ae2585 | |||
| 7579873d87 | |||
| e2ce934f2a | |||
| 56bc4a56b9 | |||
| 4bae32b57c | |||
| 6e395e3b0f | |||
| 323ad9f9e2 | |||
| 50af6499b0 | |||
| 12c22a170b | |||
| 5bbb2fc77b | |||
| 499d15e667 | |||
| bcf7dec10c | |||
| e1c7f44fec | |||
| 0e25633398 | |||
| 8097fa4da4 | |||
| ad9a1c502b | |||
| ec2e7b2ccb | |||
| 8bdeddd8f6 | |||
| e253fcb516 | |||
| 05a4f94d2f | |||
| d747e24a96 | |||
| 35e3bbeb88 | |||
| c750f6b8e9 | |||
| 0c5c762224 | |||
| 8fa78356a0 | |||
| 3fec64021a | |||
| 15528e3e9b | |||
| 39643bd8ab | |||
| d592234d4e | |||
| 67500a6023 | |||
| ffce8bd64c | |||
| 8b00f7d5ac | |||
| bbd63f4838 | |||
| bdf29afda0 | |||
| 46fa936f20 | |||
| d502696c1f | |||
| 9b43d41040 | |||
| 3276a9b6ed |
@@ -10,11 +10,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: "18"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn --frozen-lockfile
|
run: yarn --frozen-lockfile
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
esac
|
esac
|
||||||
echo "::set-output name=tag::$tag"
|
echo "::set-output name=tag::$tag"
|
||||||
- name: Build and Push Tag
|
- name: Build and Push Tag
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout 🛎️
|
- name: Checkout 🛎️
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "16"
|
node-version: "18"
|
||||||
- name: Install and Build 🔧
|
- name: Install and Build 🔧
|
||||||
run: |
|
run: |
|
||||||
yarn install
|
yarn install
|
||||||
yarn build
|
yarn build
|
||||||
|
|
||||||
- name: Deploy 🚀
|
- name: Deploy 🚀
|
||||||
uses: JamesIves/github-pages-deploy-action@v4.4.1
|
uses: JamesIves/github-pages-deploy-action@v4.4.3
|
||||||
with:
|
with:
|
||||||
branch: gh-pages
|
branch: gh-pages
|
||||||
folder: build
|
folder: build
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "16"
|
node-version: "18"
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn build
|
- run: yarn build
|
||||||
- run: |
|
- run: |
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
esac
|
esac
|
||||||
echo "::set-output name=tag::$tag"
|
echo "::set-output name=tag::$tag"
|
||||||
- name: Build and Push Tag
|
- name: Build and Push Tag
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: false
|
push: false
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
dist: focal
|
dist: focal
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- 17
|
- 18
|
||||||
|
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|||||||
+2
-3
@@ -1,14 +1,13 @@
|
|||||||
# Builder
|
# Builder
|
||||||
FROM node:lts as builder
|
FROM node:lts as builder
|
||||||
|
|
||||||
ARG PUBLIC_URL=/
|
|
||||||
ARG REACT_APP_SERVER
|
ARG REACT_APP_SERVER
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY . /src
|
COPY . /src
|
||||||
RUN yarn --network-timeout=100000 install
|
RUN yarn --network-timeout=300000 install
|
||||||
RUN PUBLIC_URL=$PUBLIC_URL REACT_APP_SERVER=$REACT_APP_SERVER yarn build
|
RUN REACT_APP_SERVER=$REACT_APP_SERVER yarn build
|
||||||
|
|
||||||
|
|
||||||
# App
|
# App
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ services:
|
|||||||
# if you're building on an architecture other than amd64, make sure
|
# if you're building on an architecture other than amd64, make sure
|
||||||
# to define a maximum ram for node. otherwise the build will fail.
|
# to define a maximum ram for node. otherwise the build will fail.
|
||||||
# - NODE_OPTIONS="--max_old_space_size=1024"
|
# - NODE_OPTIONS="--max_old_space_size=1024"
|
||||||
# default is /
|
# default is .
|
||||||
# - PUBLIC_URL=/synapse-admin
|
# - PUBLIC_URL=/synapse-admin
|
||||||
# You can use a fixed homeserver, so that the user can no longer
|
# You can use a fixed homeserver, so that the user can no longer
|
||||||
# define it himself
|
# define it himself
|
||||||
|
|||||||
+13
-14
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "synapse-admin",
|
"name": "synapse-admin",
|
||||||
"version": "0.8.5",
|
"version": "0.9.0",
|
||||||
"description": "Admin GUI for the Matrix.org server Synapse",
|
"description": "Admin GUI for the Matrix.org server Synapse",
|
||||||
"author": "Awesome Technologies Innovationslabor GmbH",
|
"author": "Awesome Technologies Innovationslabor GmbH",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -11,28 +11,27 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/react": "^11.2.6",
|
"@testing-library/react": "^12.1.5",
|
||||||
"@testing-library/user-event": "^14.4.3",
|
"@testing-library/user-event": "^14.4.3",
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.55.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"prettier": "^2.2.0",
|
"prettier": "^2.2.0"
|
||||||
"ra-test": "^3.15.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.7.1",
|
"@mui/icons-material": "^5.14.19",
|
||||||
"@emotion/styled": "^11.6.0",
|
"@mui/material": "^5.14.8",
|
||||||
"@mui/icons-material": "^5.3.1",
|
"@mui/styles": "5.14.10",
|
||||||
"@mui/material": "^5.4.0",
|
"papaparse": "^5.4.1",
|
||||||
"papaparse": "^5.2.0",
|
"prop-types": "^15.8.1",
|
||||||
"prop-types": "^15.7.2",
|
|
||||||
"ra-language-chinese": "^2.0.10",
|
"ra-language-chinese": "^2.0.10",
|
||||||
"ra-language-french": "^4.2.0",
|
"ra-language-french": "^4.16.2",
|
||||||
"ra-language-german": "^3.13.4",
|
"ra-language-german": "^3.13.4",
|
||||||
|
"ra-language-italian": "^3.13.1",
|
||||||
"react": "^17.0.0",
|
"react": "^17.0.0",
|
||||||
"react-admin": "^3.19.7",
|
"react-admin": "^4.16.9",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-scripts": "^5.0.1"
|
"react-scripts": "^5.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
User-agent: *
|
User-agent: *
|
||||||
|
Disallow: /
|
||||||
|
|||||||
+11
-4
@@ -1,5 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Admin, Resource, resolveBrowserLocale } from "react-admin";
|
import {
|
||||||
|
Admin,
|
||||||
|
CustomRoutes,
|
||||||
|
Resource,
|
||||||
|
resolveBrowserLocale,
|
||||||
|
} from "react-admin";
|
||||||
import polyglotI18nProvider from "ra-i18n-polyglot";
|
import polyglotI18nProvider from "ra-i18n-polyglot";
|
||||||
import authProvider from "./synapse/authProvider";
|
import authProvider from "./synapse/authProvider";
|
||||||
import dataProvider from "./synapse/dataProvider";
|
import dataProvider from "./synapse/dataProvider";
|
||||||
@@ -28,12 +33,14 @@ import germanMessages from "./i18n/de";
|
|||||||
import englishMessages from "./i18n/en";
|
import englishMessages from "./i18n/en";
|
||||||
import frenchMessages from "./i18n/fr";
|
import frenchMessages from "./i18n/fr";
|
||||||
import chineseMessages from "./i18n/zh";
|
import chineseMessages from "./i18n/zh";
|
||||||
|
import italianMessages from "./i18n/it";
|
||||||
|
|
||||||
// TODO: Can we use lazy loading together with browser locale?
|
// TODO: Can we use lazy loading together with browser locale?
|
||||||
const messages = {
|
const messages = {
|
||||||
de: germanMessages,
|
de: germanMessages,
|
||||||
en: englishMessages,
|
en: englishMessages,
|
||||||
fr: frenchMessages,
|
fr: frenchMessages,
|
||||||
|
it: italianMessages,
|
||||||
zh: chineseMessages,
|
zh: chineseMessages,
|
||||||
};
|
};
|
||||||
const i18nProvider = polyglotI18nProvider(
|
const i18nProvider = polyglotI18nProvider(
|
||||||
@@ -48,10 +55,10 @@ const App = () => (
|
|||||||
authProvider={authProvider}
|
authProvider={authProvider}
|
||||||
dataProvider={dataProvider}
|
dataProvider={dataProvider}
|
||||||
i18nProvider={i18nProvider}
|
i18nProvider={i18nProvider}
|
||||||
customRoutes={[
|
|
||||||
<Route key="userImport" path="/import_users" component={ImportFeature} />,
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
|
<CustomRoutes>
|
||||||
|
<Route path="/import_users" element={<ImportFeature />} />
|
||||||
|
</CustomRoutes>
|
||||||
<Resource
|
<Resource
|
||||||
name="users"
|
name="users"
|
||||||
list={UserList}
|
list={UserList}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import React from "react";
|
||||||
|
import get from "lodash/get";
|
||||||
|
import { Avatar } from "@mui/material";
|
||||||
|
import { useRecordContext } from "react-admin";
|
||||||
|
|
||||||
|
const AvatarField = ({ source, ...rest }) => {
|
||||||
|
const record = useRecordContext(rest);
|
||||||
|
const src = get(record, source)?.toString();
|
||||||
|
return <Avatar src={src} {...rest} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AvatarField;
|
||||||
@@ -79,6 +79,7 @@ export const ReportShow = props => {
|
|||||||
<ReferenceField source="sender" reference="users">
|
<ReferenceField source="sender" reference="users">
|
||||||
<TextField source="id" />
|
<TextField source="id" />
|
||||||
</ReferenceField>
|
</ReferenceField>
|
||||||
|
<TextField source="sender" label="Sender (raw user ID)" />
|
||||||
<TextField source="event_id" />
|
<TextField source="event_id" />
|
||||||
<TextField source="event_json.origin" />
|
<TextField source="event_json.origin" />
|
||||||
<TextField source="event_json.type" />
|
<TextField source="event_json.type" />
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import { useDataProvider, useNotify, Title } from "react-admin";
|
||||||
Button as ReactAdminButton,
|
|
||||||
useDataProvider,
|
|
||||||
useNotify,
|
|
||||||
Title,
|
|
||||||
} from "react-admin";
|
|
||||||
import { parse as parseCsv, unparse as unparseCsv } from "papaparse";
|
import { parse as parseCsv, unparse as unparseCsv } from "papaparse";
|
||||||
import GetAppIcon from "@mui/icons-material/GetApp";
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
@@ -23,19 +17,6 @@ import { generateRandomUser } from "./users";
|
|||||||
|
|
||||||
const LOGGING = true;
|
const LOGGING = true;
|
||||||
|
|
||||||
export const ImportButton = ({ label, variant = "text" }) => {
|
|
||||||
return (
|
|
||||||
<ReactAdminButton
|
|
||||||
color="primary"
|
|
||||||
component="span"
|
|
||||||
variant={variant}
|
|
||||||
label={label}
|
|
||||||
>
|
|
||||||
<GetAppIcon style={{ transform: "rotate(180deg)", fontSize: "20" }} />
|
|
||||||
</ReactAdminButton>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const expectedFields = ["id", "displayname"].sort();
|
const expectedFields = ["id", "displayname"].sort();
|
||||||
const optionalFields = [
|
const optionalFields = [
|
||||||
"user_type",
|
"user_type",
|
||||||
|
|||||||
+117
-128
@@ -1,19 +1,21 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
fetchUtils,
|
fetchUtils,
|
||||||
|
Form,
|
||||||
FormDataConsumer,
|
FormDataConsumer,
|
||||||
Notification,
|
Notification,
|
||||||
|
required,
|
||||||
useLogin,
|
useLogin,
|
||||||
useNotify,
|
useNotify,
|
||||||
useLocale,
|
useLocaleState,
|
||||||
useSetLocale,
|
|
||||||
useTranslate,
|
useTranslate,
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import { Form, useForm } from "react-final-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
CardActions,
|
CardActions,
|
||||||
@@ -21,66 +23,64 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
Select,
|
Select,
|
||||||
TextField,
|
TextField,
|
||||||
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { styled } from "@mui/material/styles";
|
||||||
import LockIcon from "@mui/icons-material/Lock";
|
import LockIcon from "@mui/icons-material/Lock";
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const FormBox = styled(Box)(({ theme }) => ({
|
||||||
main: {
|
display: "flex",
|
||||||
display: "flex",
|
flexDirection: "column",
|
||||||
flexDirection: "column",
|
minHeight: "calc(100vh - 1em)",
|
||||||
minHeight: "calc(100vh - 1em)",
|
alignItems: "center",
|
||||||
alignItems: "center",
|
justifyContent: "flex-start",
|
||||||
justifyContent: "flex-start",
|
background: "url(./images/floating-cogs.svg)",
|
||||||
background: "url(./images/floating-cogs.svg)",
|
backgroundColor: "#f9f9f9",
|
||||||
backgroundColor: "#f9f9f9",
|
backgroundRepeat: "no-repeat",
|
||||||
backgroundRepeat: "no-repeat",
|
backgroundSize: "cover",
|
||||||
backgroundSize: "cover",
|
|
||||||
},
|
[`& .card`]: {
|
||||||
card: {
|
|
||||||
minWidth: "30em",
|
minWidth: "30em",
|
||||||
marginTop: "6em",
|
marginTop: "6em",
|
||||||
marginBottom: "6em",
|
marginBottom: "6em",
|
||||||
},
|
},
|
||||||
avatar: {
|
[`& .avatar`]: {
|
||||||
margin: "1em",
|
margin: "1em",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
},
|
},
|
||||||
icon: {
|
[`& .icon`]: {
|
||||||
backgroundColor: theme.palette.secondary.main,
|
backgroundColor: theme.palette.grey[500],
|
||||||
},
|
},
|
||||||
hint: {
|
[`& .hint`]: {
|
||||||
marginTop: "1em",
|
marginTop: "1em",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
color: theme.palette.grey[500],
|
color: theme.palette.grey[600],
|
||||||
},
|
},
|
||||||
form: {
|
[`& .form`]: {
|
||||||
padding: "0 1em 1em 1em",
|
padding: "0 1em 1em 1em",
|
||||||
},
|
},
|
||||||
input: {
|
[`& .input`]: {
|
||||||
marginTop: "1em",
|
marginTop: "1em",
|
||||||
},
|
},
|
||||||
actions: {
|
[`& .actions`]: {
|
||||||
padding: "0 1em 1em 1em",
|
padding: "0 1em 1em 1em",
|
||||||
},
|
},
|
||||||
serverVersion: {
|
[`& .serverVersion`]: {
|
||||||
color: "#9e9e9e",
|
color: theme.palette.grey[500],
|
||||||
fontFamily: "Roboto, Helvetica, Arial, sans-serif",
|
fontFamily: "Roboto, Helvetica, Arial, sans-serif",
|
||||||
marginBottom: "1em",
|
marginBottom: "1em",
|
||||||
marginLeft: "0.5em",
|
marginLeft: "0.5em",
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const LoginPage = ({ theme }) => {
|
const LoginPage = () => {
|
||||||
const classes = useStyles({ theme });
|
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [supportPassAuth, setSupportPassAuth] = useState(true);
|
const [supportPassAuth, setSupportPassAuth] = useState(true);
|
||||||
var locale = useLocale();
|
const [locale, setLocale] = useLocaleState();
|
||||||
const setLocale = useSetLocale();
|
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
const base_url = localStorage.getItem("base_url");
|
const base_url = localStorage.getItem("base_url");
|
||||||
const cfg_base_url = process.env.REACT_APP_SERVER;
|
const cfg_base_url = process.env.REACT_APP_SERVER;
|
||||||
@@ -135,28 +135,16 @@ const LoginPage = ({ theme }) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const validate = values => {
|
const validateBaseUrl = value => {
|
||||||
const errors = {};
|
if (!value.match(/^(http|https):\/\//)) {
|
||||||
if (!values.username) {
|
return translate("synapseadmin.auth.protocol_error");
|
||||||
errors.username = translate("ra.validation.required");
|
} else if (
|
||||||
}
|
!value.match(/^(http|https):\/\/[a-zA-Z0-9\-.]+(:\d{1,5})?[^?&\s]*$/)
|
||||||
if (!values.password) {
|
) {
|
||||||
errors.password = translate("ra.validation.required");
|
return translate("synapseadmin.auth.url_error");
|
||||||
}
|
|
||||||
if (!values.base_url) {
|
|
||||||
errors.base_url = translate("ra.validation.required");
|
|
||||||
} else {
|
} else {
|
||||||
if (!values.base_url.match(/^(http|https):\/\//)) {
|
return undefined;
|
||||||
errors.base_url = translate("synapseadmin.auth.protocol_error");
|
|
||||||
} else if (
|
|
||||||
!values.base_url.match(
|
|
||||||
/^(http|https):\/\/[a-zA-Z0-9\-.]+(:\d{1,5})?[^?&\s]*$/
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
errors.base_url = translate("synapseadmin.auth.url_error");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return errors;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = auth => {
|
const handleSubmit = auth => {
|
||||||
@@ -191,7 +179,7 @@ const LoginPage = ({ theme }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const UserData = ({ formData }) => {
|
const UserData = ({ formData }) => {
|
||||||
const form = useForm();
|
const form = useFormContext();
|
||||||
const [serverVersion, setServerVersion] = useState("");
|
const [serverVersion, setServerVersion] = useState("");
|
||||||
|
|
||||||
const handleUsernameChange = _ => {
|
const handleUsernameChange = _ => {
|
||||||
@@ -204,11 +192,11 @@ const LoginPage = ({ theme }) => {
|
|||||||
fetchUtils
|
fetchUtils
|
||||||
.fetchJson(wellKnownUrl, { method: "GET" })
|
.fetchJson(wellKnownUrl, { method: "GET" })
|
||||||
.then(({ json }) => {
|
.then(({ json }) => {
|
||||||
form.change("base_url", json["m.homeserver"].base_url);
|
form.setValue("base_url", json["m.homeserver"].base_url);
|
||||||
})
|
})
|
||||||
.catch(_ => {
|
.catch(_ => {
|
||||||
// if there is no .well-known entry, try the home server name
|
// if there is no .well-known entry, try the home server name
|
||||||
form.change("base_url", `https://${home_server}`);
|
form.setValue("base_url", `https://${home_server}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -217,7 +205,9 @@ const LoginPage = ({ theme }) => {
|
|||||||
_ => {
|
_ => {
|
||||||
if (
|
if (
|
||||||
!formData.base_url ||
|
!formData.base_url ||
|
||||||
!formData.base_url.match(/^(http|https):\/\/[a-zA-Z0-9\-.]+$/)
|
!formData.base_url.match(
|
||||||
|
/^(http|https):\/\/[a-zA-Z0-9\-.]+(:\d{1,5})?$/
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
const versionUrl = `${formData.base_url}/_synapse/admin/v1/server_version`;
|
const versionUrl = `${formData.base_url}/_synapse/admin/v1/server_version`;
|
||||||
@@ -263,8 +253,8 @@ const LoginPage = ({ theme }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<div className={classes.input}>
|
<Box>
|
||||||
<TextInput
|
<TextInput
|
||||||
autoFocus
|
autoFocus
|
||||||
name="username"
|
name="username"
|
||||||
@@ -274,9 +264,11 @@ const LoginPage = ({ theme }) => {
|
|||||||
onBlur={handleUsernameChange}
|
onBlur={handleUsernameChange}
|
||||||
resettable
|
resettable
|
||||||
fullWidth
|
fullWidth
|
||||||
|
className="input"
|
||||||
|
validate={required()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Box>
|
||||||
<div className={classes.input}>
|
<Box>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
name="password"
|
name="password"
|
||||||
component={renderInput}
|
component={renderInput}
|
||||||
@@ -285,9 +277,11 @@ const LoginPage = ({ theme }) => {
|
|||||||
disabled={loading || !supportPassAuth}
|
disabled={loading || !supportPassAuth}
|
||||||
resettable
|
resettable
|
||||||
fullWidth
|
fullWidth
|
||||||
|
className="input"
|
||||||
|
validate={required()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Box>
|
||||||
<div className={classes.input}>
|
<Box>
|
||||||
<TextInput
|
<TextInput
|
||||||
name="base_url"
|
name="base_url"
|
||||||
component={renderInput}
|
component={renderInput}
|
||||||
@@ -295,80 +289,75 @@ const LoginPage = ({ theme }) => {
|
|||||||
disabled={cfg_base_url || loading}
|
disabled={cfg_base_url || loading}
|
||||||
resettable
|
resettable
|
||||||
fullWidth
|
fullWidth
|
||||||
|
className="input"
|
||||||
|
validate={[required(), validateBaseUrl]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Box>
|
||||||
<div className={classes.serverVersion}>{serverVersion}</div>
|
<Typography className="serverVersion">{serverVersion}</Typography>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
initialValues={{ base_url: cfg_base_url || base_url }}
|
defaultValues={{ base_url: cfg_base_url || base_url }}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
validate={validate}
|
mode="onTouched"
|
||||||
render={({ handleSubmit }) => (
|
>
|
||||||
<form onSubmit={handleSubmit} noValidate>
|
<FormBox>
|
||||||
<div className={classes.main}>
|
<Card className="card">
|
||||||
<Card className={classes.card}>
|
<Box className="avatar">
|
||||||
<div className={classes.avatar}>
|
<Avatar className="icon">
|
||||||
<Avatar className={classes.icon}>
|
<LockIcon />
|
||||||
<LockIcon />
|
</Avatar>
|
||||||
</Avatar>
|
</Box>
|
||||||
</div>
|
<Box className="hint">{translate("synapseadmin.auth.welcome")}</Box>
|
||||||
<div className={classes.hint}>
|
<Box className="form">
|
||||||
{translate("synapseadmin.auth.welcome")}
|
<Select
|
||||||
</div>
|
value={locale}
|
||||||
<div className={classes.form}>
|
onChange={e => {
|
||||||
<div className={classes.input}>
|
setLocale(e.target.value);
|
||||||
<Select
|
}}
|
||||||
value={locale}
|
fullWidth
|
||||||
onChange={e => {
|
disabled={loading}
|
||||||
setLocale(e.target.value);
|
className="input"
|
||||||
}}
|
>
|
||||||
fullWidth
|
<MenuItem value="de">Deutsch</MenuItem>
|
||||||
disabled={loading}
|
<MenuItem value="en">English</MenuItem>
|
||||||
>
|
<MenuItem value="fr">Français</MenuItem>
|
||||||
<MenuItem value="de">Deutsch</MenuItem>
|
<MenuItem value="it">Italiano</MenuItem>
|
||||||
<MenuItem value="en">English</MenuItem>
|
<MenuItem value="zh">简体中文</MenuItem>
|
||||||
<MenuItem value="fr">Français</MenuItem>
|
</Select>
|
||||||
<MenuItem value="zh">简体中文</MenuItem>
|
<FormDataConsumer>
|
||||||
</Select>
|
{formDataProps => <UserData {...formDataProps} />}
|
||||||
</div>
|
</FormDataConsumer>
|
||||||
<FormDataConsumer>
|
<CardActions className="actions">
|
||||||
{formDataProps => <UserData {...formDataProps} />}
|
<Button
|
||||||
</FormDataConsumer>
|
variant="contained"
|
||||||
</div>
|
type="submit"
|
||||||
<CardActions className={classes.actions}>
|
color="primary"
|
||||||
<Button
|
disabled={loading || !supportPassAuth}
|
||||||
variant="contained"
|
fullWidth
|
||||||
type="submit"
|
>
|
||||||
color="primary"
|
{loading && <CircularProgress size={25} thickness={2} />}
|
||||||
disabled={loading || !supportPassAuth}
|
{translate("ra.auth.sign_in")}
|
||||||
className={classes.button}
|
</Button>
|
||||||
fullWidth
|
<Button
|
||||||
>
|
variant="contained"
|
||||||
{loading && <CircularProgress size={25} thickness={2} />}
|
color="secondary"
|
||||||
{translate("ra.auth.sign_in")}
|
onClick={handleSSO}
|
||||||
</Button>
|
disabled={loading || ssoBaseUrl === ""}
|
||||||
<Button
|
fullWidth
|
||||||
variant="contained"
|
>
|
||||||
color="secondary"
|
{loading && <CircularProgress size={25} thickness={2} />}
|
||||||
onClick={handleSSO}
|
{translate("synapseadmin.auth.sso_sign_in")}
|
||||||
disabled={loading || ssoBaseUrl === ""}
|
</Button>
|
||||||
className={classes.button}
|
</CardActions>
|
||||||
fullWidth
|
</Box>
|
||||||
>
|
</Card>
|
||||||
{loading && <CircularProgress size={25} thickness={2} />}
|
</FormBox>
|
||||||
{translate("synapseadmin.auth.sso_sign_in")}
|
<Notification />
|
||||||
</Button>
|
</Form>
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
<Notification />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { render } from "@testing-library/react";
|
import { render } from "@testing-library/react";
|
||||||
import { TestContext } from "ra-test";
|
import { AdminContext } from "react-admin";
|
||||||
import LoginPage from "./LoginPage";
|
import LoginPage from "./LoginPage";
|
||||||
|
|
||||||
describe("LoginForm", () => {
|
describe("LoginForm", () => {
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
render(
|
render(
|
||||||
<TestContext>
|
<AdminContext>
|
||||||
<LoginPage />
|
<LoginPage />
|
||||||
</TestContext>
|
</AdminContext>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
// in src/Menu.js
|
|
||||||
import * as React from "react";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { useMediaQuery } from "@mui/material";
|
|
||||||
import { MenuItemLink, getResources } from "react-admin";
|
|
||||||
import DefaultIcon from "@mui/icons-material/ViewList";
|
|
||||||
import LabelIcon from "@mui/icons-material/Label";
|
|
||||||
|
|
||||||
const Menu = ({ onMenuClick, logout }) => {
|
|
||||||
const isXSmall = useMediaQuery(theme => theme.breakpoints.down("xs"));
|
|
||||||
const open = useSelector(state => state.admin.ui.sidebarOpen);
|
|
||||||
const resources = useSelector(getResources);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{resources.map(resource => (
|
|
||||||
<MenuItemLink
|
|
||||||
key={resource.name}
|
|
||||||
to={`/${resource.name}`}
|
|
||||||
primaryText={
|
|
||||||
(resource.options && resource.options.label) || resource.name
|
|
||||||
}
|
|
||||||
leftIcon={resource.icon ? <resource.icon /> : <DefaultIcon />}
|
|
||||||
onClick={onMenuClick}
|
|
||||||
sidebarIsOpen={open}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<MenuItemLink
|
|
||||||
to="/custom-route"
|
|
||||||
primaryText="Miscellaneous"
|
|
||||||
leftIcon={<LabelIcon />}
|
|
||||||
onClick={onMenuClick}
|
|
||||||
sidebarIsOpen={open}
|
|
||||||
/>
|
|
||||||
{isXSmall && logout}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Menu;
|
|
||||||
+80
-140
@@ -1,34 +1,28 @@
|
|||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import { Avatar, Chip } from "@mui/material";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import FolderSharedIcon from "@mui/icons-material/FolderShared";
|
import FolderSharedIcon from "@mui/icons-material/FolderShared";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
|
||||||
import {
|
import {
|
||||||
BooleanField,
|
BooleanField,
|
||||||
BulkDeleteButton,
|
BulkDeleteButton,
|
||||||
Button,
|
Button,
|
||||||
Datagrid,
|
DatagridConfigurable,
|
||||||
|
ExportButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
Filter,
|
|
||||||
List,
|
List,
|
||||||
NumberField,
|
NumberField,
|
||||||
Pagination,
|
Pagination,
|
||||||
|
SelectColumnsButton,
|
||||||
TextField,
|
TextField,
|
||||||
|
TopToolbar,
|
||||||
useCreate,
|
useCreate,
|
||||||
useMutation,
|
useListContext,
|
||||||
useNotify,
|
useNotify,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
useRecordContext,
|
useRecordContext,
|
||||||
useRefresh,
|
useRefresh,
|
||||||
useUnselectAll,
|
useUnselectAll,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
|
import { useMutation } from "react-query";
|
||||||
const useStyles = makeStyles({
|
import AvatarField from "./AvatarField";
|
||||||
small: {
|
|
||||||
height: "40px",
|
|
||||||
width: "40px",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const RoomDirectoryPagination = props => (
|
const RoomDirectoryPagination = props => (
|
||||||
<Pagination {...props} rowsPerPageOptions={[100, 500, 1000, 2000]} />
|
<Pagination {...props} rowsPerPageOptions={[100, 500, 1000, 2000]} />
|
||||||
@@ -67,26 +61,23 @@ export const RoomDirectoryBulkDeleteButton = props => (
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const RoomDirectoryBulkSaveButton = ({ selectedIds }) => {
|
export const RoomDirectoryBulkSaveButton = () => {
|
||||||
|
const { selectedIds } = useListContext();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
const unselectAll = useUnselectAll();
|
const unselectAll = useUnselectAll();
|
||||||
const [createMany, { loading }] = useMutation();
|
const { createMany, isloading } = useMutation();
|
||||||
|
|
||||||
const handleSend = values => {
|
const handleSend = values => {
|
||||||
createMany(
|
createMany(
|
||||||
|
["room_directory", "createMany", { ids: selectedIds, data: {} }],
|
||||||
{
|
{
|
||||||
type: "createMany",
|
onSuccess: data => {
|
||||||
resource: "room_directory",
|
|
||||||
payload: { ids: selectedIds, data: {} },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: ({ data }) => {
|
|
||||||
notify("resources.room_directory.action.send_success");
|
notify("resources.room_directory.action.send_success");
|
||||||
unselectAll("rooms");
|
unselectAll("rooms");
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
onFailure: error =>
|
onError: error =>
|
||||||
notify("resources.room_directory.action.send_failure", {
|
notify("resources.room_directory.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -98,30 +89,29 @@ export const RoomDirectoryBulkSaveButton = ({ selectedIds }) => {
|
|||||||
<Button
|
<Button
|
||||||
label="resources.room_directory.action.create"
|
label="resources.room_directory.action.create"
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={loading}
|
disabled={isloading}
|
||||||
>
|
>
|
||||||
<FolderSharedIcon />
|
<FolderSharedIcon />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RoomDirectorySaveButton = props => {
|
export const RoomDirectorySaveButton = () => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
const [create, { loading }] = useCreate("room_directory");
|
const [create, { isloading }] = useCreate();
|
||||||
|
|
||||||
const handleSend = values => {
|
const handleSend = values => {
|
||||||
create(
|
create(
|
||||||
|
"room_directory",
|
||||||
|
{ data: { id: record.id } },
|
||||||
{
|
{
|
||||||
payload: { data: { id: record.id } },
|
onSuccess: data => {
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: ({ data }) => {
|
|
||||||
notify("resources.room_directory.action.send_success");
|
notify("resources.room_directory.action.send_success");
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
onFailure: error =>
|
onError: error =>
|
||||||
notify("resources.room_directory.action.send_failure", {
|
notify("resources.room_directory.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -133,128 +123,78 @@ export const RoomDirectorySaveButton = props => {
|
|||||||
<Button
|
<Button
|
||||||
label="resources.room_directory.action.create"
|
label="resources.room_directory.action.create"
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={loading}
|
disabled={isloading}
|
||||||
>
|
>
|
||||||
<FolderSharedIcon />
|
<FolderSharedIcon />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoomDirectoryBulkActionButtons = props => (
|
const RoomDirectoryBulkActionButtons = () => (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<RoomDirectoryBulkDeleteButton {...props} />
|
<RoomDirectoryBulkDeleteButton />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
const AvatarField = ({ source, className, record = {} }) => (
|
const RoomDirectoryListActions = () => (
|
||||||
<Avatar src={record[source]} className={className} />
|
<TopToolbar>
|
||||||
|
<SelectColumnsButton />
|
||||||
|
<ExportButton />
|
||||||
|
</TopToolbar>
|
||||||
);
|
);
|
||||||
|
|
||||||
const RoomDirectoryFilter = ({ ...props }) => {
|
export const RoomDirectoryList = () => (
|
||||||
const translate = useTranslate();
|
<List
|
||||||
return (
|
pagination={<RoomDirectoryPagination />}
|
||||||
<Filter {...props}>
|
perPage={100}
|
||||||
<Chip
|
actions={<RoomDirectoryListActions />}
|
||||||
label={translate("resources.rooms.fields.room_id")}
|
>
|
||||||
source="room_id"
|
<DatagridConfigurable
|
||||||
defaultValue={false}
|
rowClick={(id, resource, record) => "/rooms/" + id + "/show"}
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Chip
|
|
||||||
label={translate("resources.rooms.fields.topic")}
|
|
||||||
source="topic"
|
|
||||||
defaultValue={false}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Chip
|
|
||||||
label={translate("resources.rooms.fields.canonical_alias")}
|
|
||||||
source="canonical_alias"
|
|
||||||
defaultValue={false}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
</Filter>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FilterableRoomDirectoryList = ({
|
|
||||||
roomDirectoryFilters,
|
|
||||||
dispatch,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const classes = useStyles();
|
|
||||||
const filter = roomDirectoryFilters;
|
|
||||||
const roomIdFilter = filter && filter.room_id ? true : false;
|
|
||||||
const topicFilter = filter && filter.topic ? true : false;
|
|
||||||
const canonicalAliasFilter = filter && filter.canonical_alias ? true : false;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<List
|
|
||||||
{...props}
|
|
||||||
pagination={<RoomDirectoryPagination />}
|
|
||||||
bulkActionButtons={<RoomDirectoryBulkActionButtons />}
|
bulkActionButtons={<RoomDirectoryBulkActionButtons />}
|
||||||
filters={<RoomDirectoryFilter />}
|
omit={["room_id", "canonical_alias", "topic"]}
|
||||||
perPage={100}
|
|
||||||
>
|
>
|
||||||
<Datagrid rowClick={(id, basePath, record) => "/rooms/" + id + "/show"}>
|
<AvatarField
|
||||||
<AvatarField
|
source="avatar_src"
|
||||||
source="avatar_src"
|
sortable={false}
|
||||||
sortable={false}
|
sx={{ height: "40px", width: "40px" }}
|
||||||
className={classes.small}
|
label="resources.rooms.fields.avatar"
|
||||||
label="resources.rooms.fields.avatar"
|
/>
|
||||||
/>
|
<TextField
|
||||||
<TextField
|
source="name"
|
||||||
source="name"
|
sortable={false}
|
||||||
sortable={false}
|
label="resources.rooms.fields.name"
|
||||||
label="resources.rooms.fields.name"
|
/>
|
||||||
/>
|
<TextField
|
||||||
{roomIdFilter && (
|
source="room_id"
|
||||||
<TextField
|
sortable={false}
|
||||||
source="room_id"
|
label="resources.rooms.fields.room_id"
|
||||||
sortable={false}
|
/>
|
||||||
label="resources.rooms.fields.room_id"
|
<TextField
|
||||||
/>
|
source="canonical_alias"
|
||||||
)}
|
sortable={false}
|
||||||
{canonicalAliasFilter && (
|
label="resources.rooms.fields.canonical_alias"
|
||||||
<TextField
|
/>
|
||||||
source="canonical_alias"
|
<TextField
|
||||||
sortable={false}
|
source="topic"
|
||||||
label="resources.rooms.fields.canonical_alias"
|
sortable={false}
|
||||||
/>
|
label="resources.rooms.fields.topic"
|
||||||
)}
|
/>
|
||||||
{topicFilter && (
|
<NumberField
|
||||||
<TextField
|
source="num_joined_members"
|
||||||
source="topic"
|
sortable={false}
|
||||||
sortable={false}
|
label="resources.rooms.fields.joined_members"
|
||||||
label="resources.rooms.fields.topic"
|
/>
|
||||||
/>
|
<BooleanField
|
||||||
)}
|
source="world_readable"
|
||||||
<NumberField
|
sortable={false}
|
||||||
source="num_joined_members"
|
label="resources.room_directory.fields.world_readable"
|
||||||
sortable={false}
|
/>
|
||||||
label="resources.rooms.fields.joined_members"
|
<BooleanField
|
||||||
/>
|
source="guest_can_join"
|
||||||
<BooleanField
|
sortable={false}
|
||||||
source="world_readable"
|
label="resources.room_directory.fields.guest_can_join"
|
||||||
sortable={false}
|
/>
|
||||||
label="resources.room_directory.fields.world_readable"
|
</DatagridConfigurable>
|
||||||
/>
|
</List>
|
||||||
<BooleanField
|
|
||||||
source="guest_can_join"
|
|
||||||
sortable={false}
|
|
||||||
label="resources.room_directory.fields.guest_can_join"
|
|
||||||
/>
|
|
||||||
</Datagrid>
|
|
||||||
</List>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
|
||||||
return {
|
|
||||||
roomDirectoryFilters:
|
|
||||||
state.admin.resources.room_directory.list.params.displayedFilters,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RoomDirectoryList = connect(mapStateToProps)(
|
|
||||||
FilterableRoomDirectoryList
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import {
|
|||||||
Toolbar,
|
Toolbar,
|
||||||
required,
|
required,
|
||||||
useCreate,
|
useCreate,
|
||||||
useMutation,
|
useListContext,
|
||||||
useNotify,
|
useNotify,
|
||||||
useRecordContext,
|
useRecordContext,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
useUnselectAll,
|
useUnselectAll,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
|
import { useMutation } from "react-query";
|
||||||
import MessageIcon from "@mui/icons-material/Message";
|
import MessageIcon from "@mui/icons-material/Message";
|
||||||
import IconCancel from "@mui/icons-material/Cancel";
|
import IconCancel from "@mui/icons-material/Cancel";
|
||||||
import {
|
import {
|
||||||
@@ -48,7 +49,6 @@ const ServerNoticeDialog = ({ open, loading, onClose, onSend }) => {
|
|||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<SimpleForm
|
<SimpleForm
|
||||||
toolbar={<ServerNoticeToolbar />}
|
toolbar={<ServerNoticeToolbar />}
|
||||||
submitOnEnter={false}
|
|
||||||
redirect={false}
|
redirect={false}
|
||||||
save={onSend}
|
save={onSend}
|
||||||
>
|
>
|
||||||
@@ -67,11 +67,11 @@ const ServerNoticeDialog = ({ open, loading, onClose, onSend }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ServerNoticeButton = props => {
|
export const ServerNoticeButton = () => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const [create, { loading }] = useCreate("servernotices");
|
const [create, { isloading }] = useCreate("servernotices");
|
||||||
|
|
||||||
const handleDialogOpen = () => setOpen(true);
|
const handleDialogOpen = () => setOpen(true);
|
||||||
const handleDialogClose = () => setOpen(false);
|
const handleDialogClose = () => setOpen(false);
|
||||||
@@ -84,7 +84,7 @@ export const ServerNoticeButton = props => {
|
|||||||
notify("resources.servernotices.action.send_success");
|
notify("resources.servernotices.action.send_success");
|
||||||
handleDialogClose();
|
handleDialogClose();
|
||||||
},
|
},
|
||||||
onFailure: () =>
|
onError: () =>
|
||||||
notify("resources.servernotices.action.send_failure", {
|
notify("resources.servernotices.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -97,7 +97,7 @@ export const ServerNoticeButton = props => {
|
|||||||
<Button
|
<Button
|
||||||
label="resources.servernotices.send"
|
label="resources.servernotices.send"
|
||||||
onClick={handleDialogOpen}
|
onClick={handleDialogOpen}
|
||||||
disabled={loading}
|
disabled={isloading}
|
||||||
>
|
>
|
||||||
<MessageIcon />
|
<MessageIcon />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -110,29 +110,26 @@ export const ServerNoticeButton = props => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ServerNoticeBulkButton = ({ selectedIds }) => {
|
export const ServerNoticeBulkButton = () => {
|
||||||
|
const { selectedIds } = useListContext();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const unselectAll = useUnselectAll();
|
const unselectAll = useUnselectAll();
|
||||||
const [createMany, { loading }] = useMutation();
|
const { createMany, isloading } = useMutation();
|
||||||
|
|
||||||
const handleDialogOpen = () => setOpen(true);
|
const handleDialogOpen = () => setOpen(true);
|
||||||
const handleDialogClose = () => setOpen(false);
|
const handleDialogClose = () => setOpen(false);
|
||||||
|
|
||||||
const handleSend = values => {
|
const handleSend = values => {
|
||||||
createMany(
|
createMany(
|
||||||
|
["servernotices", "createMany", { ids: selectedIds, data: values }],
|
||||||
{
|
{
|
||||||
type: "createMany",
|
onSuccess: data => {
|
||||||
resource: "servernotices",
|
|
||||||
payload: { ids: selectedIds, data: values },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: ({ data }) => {
|
|
||||||
notify("resources.servernotices.action.send_success");
|
notify("resources.servernotices.action.send_success");
|
||||||
unselectAll("users");
|
unselectAll("users");
|
||||||
handleDialogClose();
|
handleDialogClose();
|
||||||
},
|
},
|
||||||
onFailure: error =>
|
onError: error =>
|
||||||
notify("resources.servernotices.action.send_failure", {
|
notify("resources.servernotices.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -145,7 +142,7 @@ export const ServerNoticeBulkButton = ({ selectedIds }) => {
|
|||||||
<Button
|
<Button
|
||||||
label="resources.servernotices.send"
|
label="resources.servernotices.send"
|
||||||
onClick={handleDialogOpen}
|
onClick={handleDialogOpen}
|
||||||
disabled={loading}
|
disabled={isloading}
|
||||||
>
|
>
|
||||||
<MessageIcon />
|
<MessageIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ import {
|
|||||||
useRefresh,
|
useRefresh,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import AutorenewIcon from "@material-ui/icons/Autorenew";
|
import AutorenewIcon from "@mui/icons-material/Autorenew";
|
||||||
import FolderSharedIcon from "@material-ui/icons/FolderShared";
|
import FolderSharedIcon from "@mui/icons-material/FolderShared";
|
||||||
import ViewListIcon from "@material-ui/icons/ViewList";
|
import ViewListIcon from "@mui/icons-material/ViewList";
|
||||||
|
|
||||||
const DestinationPagination = props => (
|
const DestinationPagination = props => (
|
||||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||||
@@ -37,11 +37,11 @@ const date_format = {
|
|||||||
second: "2-digit",
|
second: "2-digit",
|
||||||
};
|
};
|
||||||
|
|
||||||
const destinationRowStyle = (record, index) => ({
|
const destinationRowSx = (record, _index) => ({
|
||||||
backgroundColor: record.retry_last_ts > 0 ? "#ffcccc" : "white",
|
backgroundColor: record.retry_last_ts > 0 ? "#ffcccc" : "white",
|
||||||
});
|
});
|
||||||
|
|
||||||
const DestinationFilter = ({ ...props }) => {
|
const DestinationFilter = props => {
|
||||||
return (
|
return (
|
||||||
<Filter {...props}>
|
<Filter {...props}>
|
||||||
<SearchInput source="destination" alwaysOn />
|
<SearchInput source="destination" alwaysOn />
|
||||||
@@ -71,7 +71,7 @@ export const DestinationReconnectButton = props => {
|
|||||||
});
|
});
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
onFailure: () => {
|
onError: () => {
|
||||||
notify("ra.message.error", { type: "error" });
|
notify("ra.message.error", { type: "error" });
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -112,11 +112,11 @@ export const DestinationList = props => {
|
|||||||
filters={<DestinationFilter />}
|
filters={<DestinationFilter />}
|
||||||
pagination={<DestinationPagination />}
|
pagination={<DestinationPagination />}
|
||||||
sort={{ field: "destination", order: "ASC" }}
|
sort={{ field: "destination", order: "ASC" }}
|
||||||
bulkActionButtons={false}
|
|
||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
rowStyle={destinationRowStyle}
|
rowSx={destinationRowSx}
|
||||||
rowClick={(id, basePath, record) => `${basePath}/${id}/show/rooms`}
|
rowClick={(id, _resource, _record) => `${id}/show/rooms`}
|
||||||
|
bulkActionButtons={false}
|
||||||
>
|
>
|
||||||
<TextField source="destination" />
|
<TextField source="destination" />
|
||||||
<DateField source="failure_ts" showTime options={date_format} />
|
<DateField source="failure_ts" showTime options={date_format} />
|
||||||
@@ -160,7 +160,7 @@ export const DestinationShow = props => {
|
|||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
rowClick={(id, basePath, record) => `/rooms/${id}/show`}
|
rowClick={(id, resource, record) => `/rooms/${id}/show`}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
source="room_id"
|
source="room_id"
|
||||||
|
|||||||
+12
-21
@@ -8,29 +8,11 @@ import {
|
|||||||
useRefresh,
|
useRefresh,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import ActionDelete from "@mui/icons-material/Delete";
|
import ActionDelete from "@mui/icons-material/Delete";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { alpha, useTheme } from "@mui/material/styles";
|
||||||
import { alpha } from "@mui/material/styles";
|
|
||||||
import classnames from "classnames";
|
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => ({
|
|
||||||
deleteButton: {
|
|
||||||
color: theme.palette.error.main,
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
|
||||||
// Reset on mouse devices
|
|
||||||
"@media (hover: none)": {
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{ name: "RaDeleteDeviceButton" }
|
|
||||||
);
|
|
||||||
|
|
||||||
export const DeviceRemoveButton = props => {
|
export const DeviceRemoveButton = props => {
|
||||||
|
const theme = useTheme();
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const classes = useStyles(props);
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
@@ -63,7 +45,16 @@ export const DeviceRemoveButton = props => {
|
|||||||
<Button
|
<Button
|
||||||
label="ra.action.remove"
|
label="ra.action.remove"
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className={classnames("ra-delete-button", classes.deleteButton)}
|
sx={{
|
||||||
|
color: theme.palette.error.main,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
||||||
|
// Reset on mouse devices
|
||||||
|
"@media (hover: none)": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ActionDelete />
|
<ActionDelete />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
+19
-29
@@ -1,7 +1,4 @@
|
|||||||
import React, { Fragment, useState } from "react";
|
import React, { Fragment, useState } from "react";
|
||||||
import classnames from "classnames";
|
|
||||||
import { alpha } from "@mui/material/styles";
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
|
||||||
import {
|
import {
|
||||||
BooleanInput,
|
BooleanInput,
|
||||||
Button,
|
Button,
|
||||||
@@ -30,22 +27,7 @@ import {
|
|||||||
import IconCancel from "@mui/icons-material/Cancel";
|
import IconCancel from "@mui/icons-material/Cancel";
|
||||||
import LockIcon from "@mui/icons-material/Lock";
|
import LockIcon from "@mui/icons-material/Lock";
|
||||||
import LockOpenIcon from "@mui/icons-material/LockOpen";
|
import LockOpenIcon from "@mui/icons-material/LockOpen";
|
||||||
|
import { alpha, useTheme } from "@mui/material/styles";
|
||||||
const useStyles = makeStyles(
|
|
||||||
theme => ({
|
|
||||||
deleteButton: {
|
|
||||||
color: theme.palette.error.main,
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
|
||||||
// Reset on mouse devices
|
|
||||||
"@media (hover: none)": {
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
{ name: "RaDeleteDeviceButton" }
|
|
||||||
);
|
|
||||||
|
|
||||||
const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
@@ -81,7 +63,6 @@ const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
|||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<SimpleForm
|
<SimpleForm
|
||||||
toolbar={<DeleteMediaToolbar />}
|
toolbar={<DeleteMediaToolbar />}
|
||||||
submitOnEnter={false}
|
|
||||||
redirect={false}
|
redirect={false}
|
||||||
save={onSend}
|
save={onSend}
|
||||||
>
|
>
|
||||||
@@ -113,10 +94,10 @@ const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteMediaButton = props => {
|
export const DeleteMediaButton = props => {
|
||||||
const classes = useStyles(props);
|
const theme = useTheme();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const [deleteOne, { loading }] = useDelete("delete_media");
|
const [deleteOne, { isLoading }] = useDelete("delete_media");
|
||||||
|
|
||||||
const handleDialogOpen = () => setOpen(true);
|
const handleDialogOpen = () => setOpen(true);
|
||||||
const handleDialogClose = () => setOpen(false);
|
const handleDialogClose = () => setOpen(false);
|
||||||
@@ -129,7 +110,7 @@ export const DeleteMediaButton = props => {
|
|||||||
notify("resources.delete_media.action.send_success");
|
notify("resources.delete_media.action.send_success");
|
||||||
handleDialogClose();
|
handleDialogClose();
|
||||||
},
|
},
|
||||||
onFailure: () =>
|
onError: () =>
|
||||||
notify("resources.delete_media.action.send_failure", {
|
notify("resources.delete_media.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -142,8 +123,17 @@ export const DeleteMediaButton = props => {
|
|||||||
<Button
|
<Button
|
||||||
label="resources.delete_media.action.send"
|
label="resources.delete_media.action.send"
|
||||||
onClick={handleDialogOpen}
|
onClick={handleDialogOpen}
|
||||||
disabled={loading}
|
disabled={isLoading}
|
||||||
className={classnames("ra-delete-button", classes.deleteButton)}
|
sx={{
|
||||||
|
color: theme.palette.error.main,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
||||||
|
// Reset on mouse devices
|
||||||
|
"@media (hover: none)": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<DeleteSweepIcon />
|
<DeleteSweepIcon />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -174,7 +164,7 @@ export const ProtectMediaButton = props => {
|
|||||||
notify("resources.protect_media.action.send_success");
|
notify("resources.protect_media.action.send_success");
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
onFailure: () =>
|
onError: () =>
|
||||||
notify("resources.protect_media.action.send_failure", {
|
notify("resources.protect_media.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -190,7 +180,7 @@ export const ProtectMediaButton = props => {
|
|||||||
notify("resources.protect_media.action.send_success");
|
notify("resources.protect_media.action.send_success");
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
onFailure: () =>
|
onError: () =>
|
||||||
notify("resources.protect_media.action.send_failure", {
|
notify("resources.protect_media.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -270,7 +260,7 @@ export const QuarantineMediaButton = props => {
|
|||||||
notify("resources.quarantine_media.action.send_success");
|
notify("resources.quarantine_media.action.send_success");
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
onFailure: () =>
|
onError: () =>
|
||||||
notify("resources.quarantine_media.action.send_failure", {
|
notify("resources.quarantine_media.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
@@ -286,7 +276,7 @@ export const QuarantineMediaButton = props => {
|
|||||||
notify("resources.quarantine_media.action.send_success");
|
notify("resources.quarantine_media.action.send_success");
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
onFailure: () =>
|
onError: () =>
|
||||||
notify("resources.quarantine_media.action.send_failure", {
|
notify("resources.quarantine_media.action.send_failure", {
|
||||||
type: "error",
|
type: "error",
|
||||||
}),
|
}),
|
||||||
|
|||||||
+60
-118
@@ -1,18 +1,21 @@
|
|||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import { connect } from "react-redux";
|
|
||||||
import {
|
import {
|
||||||
BooleanField,
|
BooleanField,
|
||||||
BulkDeleteButton,
|
BulkDeleteButton,
|
||||||
DateField,
|
DateField,
|
||||||
Datagrid,
|
Datagrid,
|
||||||
|
DatagridConfigurable,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
|
ExportButton,
|
||||||
Filter,
|
Filter,
|
||||||
|
FunctionField,
|
||||||
List,
|
List,
|
||||||
NumberField,
|
NumberField,
|
||||||
Pagination,
|
Pagination,
|
||||||
ReferenceField,
|
ReferenceField,
|
||||||
ReferenceManyField,
|
ReferenceManyField,
|
||||||
SearchInput,
|
SearchInput,
|
||||||
|
SelectColumnsButton,
|
||||||
SelectField,
|
SelectField,
|
||||||
Show,
|
Show,
|
||||||
Tab,
|
Tab,
|
||||||
@@ -22,10 +25,8 @@ import {
|
|||||||
useRecordContext,
|
useRecordContext,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import get from "lodash/get";
|
import { useTheme } from "@mui/material/styles";
|
||||||
import PropTypes from "prop-types";
|
import Box from "@mui/material/Box";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
|
||||||
import { Tooltip, Typography, Chip } from "@mui/material";
|
|
||||||
import FastForwardIcon from "@mui/icons-material/FastForward";
|
import FastForwardIcon from "@mui/icons-material/FastForward";
|
||||||
import HttpsIcon from "@mui/icons-material/Https";
|
import HttpsIcon from "@mui/icons-material/Https";
|
||||||
import NoEncryptionIcon from "@mui/icons-material/NoEncryption";
|
import NoEncryptionIcon from "@mui/icons-material/NoEncryption";
|
||||||
@@ -50,43 +51,10 @@ const date_format = {
|
|||||||
second: "2-digit",
|
second: "2-digit",
|
||||||
};
|
};
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
|
||||||
helper_forward_extremities: {
|
|
||||||
fontFamily: "Roboto, Helvetica, Arial, sans-serif",
|
|
||||||
margin: "0.5em",
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const RoomPagination = props => (
|
const RoomPagination = props => (
|
||||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const EncryptionField = ({ source, record = {}, emptyText }) => {
|
|
||||||
const translate = useTranslate();
|
|
||||||
const value = get(record, source);
|
|
||||||
let ariaLabel = value === false ? "ra.boolean.false" : "ra.boolean.true";
|
|
||||||
|
|
||||||
if (value === false || value === true) {
|
|
||||||
return (
|
|
||||||
<Typography component="span" variant="body2">
|
|
||||||
<Tooltip title={translate(ariaLabel, { _: ariaLabel })}>
|
|
||||||
{value === true ? (
|
|
||||||
<HttpsIcon data-testid="true" htmlColor="limegreen" />
|
|
||||||
) : (
|
|
||||||
<NoEncryptionIcon data-testid="false" color="error" />
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Typography component="span" variant="body2">
|
|
||||||
{emptyText}
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const RoomTitle = props => {
|
const RoomTitle = props => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
@@ -102,7 +70,7 @@ const RoomTitle = props => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoomShowActions = ({ basePath, data, resource }) => {
|
const RoomShowActions = ({ data, resource }) => {
|
||||||
var roomDirectoryStatus = "";
|
var roomDirectoryStatus = "";
|
||||||
if (data) {
|
if (data) {
|
||||||
roomDirectoryStatus = data.public;
|
roomDirectoryStatus = data.public;
|
||||||
@@ -117,7 +85,6 @@ const RoomShowActions = ({ basePath, data, resource }) => {
|
|||||||
<RoomDirectoryDeleteButton record={data} />
|
<RoomDirectoryDeleteButton record={data} />
|
||||||
)}
|
)}
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
basePath={basePath}
|
|
||||||
record={data}
|
record={data}
|
||||||
resource={resource}
|
resource={resource}
|
||||||
mutationMode="pessimistic"
|
mutationMode="pessimistic"
|
||||||
@@ -129,7 +96,6 @@ const RoomShowActions = ({ basePath, data, resource }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RoomShow = props => {
|
export const RoomShow = props => {
|
||||||
const classes = useStyles({ props });
|
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
|
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
|
||||||
@@ -171,7 +137,7 @@ export const RoomShow = props => {
|
|||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
rowClick={(id, basePath, record) => "/users/" + id}
|
rowClick={(id, resource, record) => "/users/" + id}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
source="id"
|
source="id"
|
||||||
@@ -281,9 +247,14 @@ export const RoomShow = props => {
|
|||||||
icon={<FastForwardIcon />}
|
icon={<FastForwardIcon />}
|
||||||
path="forward_extremities"
|
path="forward_extremities"
|
||||||
>
|
>
|
||||||
<div className={classes.helper_forward_extremities}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
fontFamily: "Roboto, Helvetica, Arial, sans-serif",
|
||||||
|
margin: "0.5em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
{translate("resources.rooms.helper.forward_extremities")}
|
{translate("resources.rooms.helper.forward_extremities")}
|
||||||
</div>
|
</Box>
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="forward_extremities"
|
reference="forward_extremities"
|
||||||
target="room_id"
|
target="room_id"
|
||||||
@@ -307,12 +278,11 @@ export const RoomShow = props => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoomBulkActionButtons = props => (
|
const RoomBulkActionButtons = () => (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<RoomDirectoryBulkSaveButton {...props} />
|
<RoomDirectoryBulkSaveButton />
|
||||||
<RoomDirectoryBulkDeleteButton {...props} />
|
<RoomDirectoryBulkDeleteButton />
|
||||||
<BulkDeleteButton
|
<BulkDeleteButton
|
||||||
{...props}
|
|
||||||
confirmTitle="resources.rooms.action.erase.title"
|
confirmTitle="resources.rooms.action.erase.title"
|
||||||
confirmContent="resources.rooms.action.erase.content"
|
confirmContent="resources.rooms.action.erase.content"
|
||||||
mutationMode="pessimistic"
|
mutationMode="pessimistic"
|
||||||
@@ -320,91 +290,63 @@ const RoomBulkActionButtons = props => (
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
const RoomFilter = ({ ...props }) => {
|
const RoomFilter = props => (
|
||||||
const translate = useTranslate();
|
<Filter {...props}>
|
||||||
return (
|
<SearchInput source="search_term" alwaysOn />
|
||||||
<Filter {...props}>
|
</Filter>
|
||||||
<SearchInput source="search_term" alwaysOn />
|
);
|
||||||
<Chip
|
|
||||||
label={translate("resources.rooms.fields.joined_local_members")}
|
|
||||||
source="joined_local_members"
|
|
||||||
defaultValue={false}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Chip
|
|
||||||
label={translate("resources.rooms.fields.state_events")}
|
|
||||||
source="state_events"
|
|
||||||
defaultValue={false}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Chip
|
|
||||||
label={translate("resources.rooms.fields.version")}
|
|
||||||
source="version"
|
|
||||||
defaultValue={false}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
<Chip
|
|
||||||
label={translate("resources.rooms.fields.federatable")}
|
|
||||||
source="federatable"
|
|
||||||
defaultValue={false}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
/>
|
|
||||||
</Filter>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const RoomNameField = props => {
|
const RoomListActions = () => (
|
||||||
const { source } = props;
|
<TopToolbar>
|
||||||
const record = useRecordContext();
|
<SelectColumnsButton />
|
||||||
return (
|
<ExportButton />
|
||||||
<span>{record[source] || record["canonical_alias"] || record["id"]}</span>
|
</TopToolbar>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
RoomNameField.propTypes = {
|
export const RoomList = () => {
|
||||||
label: PropTypes.string,
|
const theme = useTheme();
|
||||||
record: PropTypes.object,
|
|
||||||
source: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const FilterableRoomList = ({ roomFilters, dispatch, ...props }) => {
|
|
||||||
const filter = roomFilters;
|
|
||||||
const localMembersFilter =
|
|
||||||
filter && filter.joined_local_members ? true : false;
|
|
||||||
const stateEventsFilter = filter && filter.state_events ? true : false;
|
|
||||||
const versionFilter = filter && filter.version ? true : false;
|
|
||||||
const federateableFilter = filter && filter.federatable ? true : false;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List
|
<List
|
||||||
{...props}
|
|
||||||
pagination={<RoomPagination />}
|
pagination={<RoomPagination />}
|
||||||
sort={{ field: "name", order: "ASC" }}
|
sort={{ field: "name", order: "ASC" }}
|
||||||
filters={<RoomFilter />}
|
filters={<RoomFilter />}
|
||||||
bulkActionButtons={<RoomBulkActionButtons />}
|
actions={<RoomListActions />}
|
||||||
>
|
>
|
||||||
<Datagrid rowClick="show">
|
<DatagridConfigurable
|
||||||
<EncryptionField
|
rowClick="show"
|
||||||
|
bulkActionButtons={<RoomBulkActionButtons />}
|
||||||
|
omit={[
|
||||||
|
"joined_local_members",
|
||||||
|
"state_events",
|
||||||
|
"version",
|
||||||
|
"federatable",
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<BooleanField
|
||||||
source="is_encrypted"
|
source="is_encrypted"
|
||||||
sortBy="encryption"
|
sortBy="encryption"
|
||||||
|
TrueIcon={HttpsIcon}
|
||||||
|
FalseIcon={NoEncryptionIcon}
|
||||||
label={<HttpsIcon />}
|
label={<HttpsIcon />}
|
||||||
|
sx={{
|
||||||
|
[`& [data-testid="true"]`]: { color: theme.palette.success.main },
|
||||||
|
[`& [data-testid="false"]`]: { color: theme.palette.error.main },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FunctionField
|
||||||
|
source="name"
|
||||||
|
render={record =>
|
||||||
|
record["name"] || record["canonical_alias"] || record["id"]
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<RoomNameField source="name" />
|
|
||||||
<TextField source="joined_members" />
|
<TextField source="joined_members" />
|
||||||
{localMembersFilter && <TextField source="joined_local_members" />}
|
<TextField source="joined_local_members" />
|
||||||
{stateEventsFilter && <TextField source="state_events" />}
|
<TextField source="state_events" />
|
||||||
{versionFilter && <TextField source="version" />}
|
<TextField source="version" />
|
||||||
{federateableFilter && <BooleanField source="federatable" />}
|
<BooleanField source="federatable" />
|
||||||
<BooleanField source="public" />
|
<BooleanField source="public" />
|
||||||
</Datagrid>
|
</DatagridConfigurable>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
|
||||||
return {
|
|
||||||
roomFilters: state.admin.resources.rooms.list.params.displayedFilters,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RoomList = connect(mapStateToProps)(FilterableRoomList);
|
|
||||||
|
|||||||
@@ -65,9 +65,11 @@ export const UserMediaStatsList = props => {
|
|||||||
filters={<UserMediaStatsFilter />}
|
filters={<UserMediaStatsFilter />}
|
||||||
pagination={<UserMediaStatsPagination />}
|
pagination={<UserMediaStatsPagination />}
|
||||||
sort={{ field: "media_length", order: "DESC" }}
|
sort={{ field: "media_length", order: "DESC" }}
|
||||||
bulkActionButtons={false}
|
|
||||||
>
|
>
|
||||||
<Datagrid rowClick={(id, basePath, record) => "/users/" + id + "/media"}>
|
<Datagrid
|
||||||
|
rowClick={(id, resource, record) => "/users/" + id + "/media"}
|
||||||
|
bulkActionButtons={false}
|
||||||
|
>
|
||||||
<TextField source="user_id" label="resources.users.fields.id" />
|
<TextField source="user_id" label="resources.users.fields.id" />
|
||||||
<TextField
|
<TextField
|
||||||
source="displayname"
|
source="displayname"
|
||||||
|
|||||||
+9
-38
@@ -1,5 +1,4 @@
|
|||||||
import React, { cloneElement, Fragment } from "react";
|
import React, { cloneElement, Fragment } from "react";
|
||||||
import Avatar from "@mui/material/Avatar";
|
|
||||||
import AssignmentIndIcon from "@mui/icons-material/AssignmentInd";
|
import AssignmentIndIcon from "@mui/icons-material/AssignmentInd";
|
||||||
import ContactMailIcon from "@mui/icons-material/ContactMail";
|
import ContactMailIcon from "@mui/icons-material/ContactMail";
|
||||||
import DevicesIcon from "@mui/icons-material/Devices";
|
import DevicesIcon from "@mui/icons-material/Devices";
|
||||||
@@ -49,28 +48,10 @@ import {
|
|||||||
NumberField,
|
NumberField,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import AvatarField from "./AvatarField";
|
||||||
import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices";
|
import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices";
|
||||||
import { DeviceRemoveButton } from "./devices";
|
import { DeviceRemoveButton } from "./devices";
|
||||||
import { ProtectMediaButton, QuarantineMediaButton } from "./media";
|
import { ProtectMediaButton, QuarantineMediaButton } from "./media";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
|
||||||
|
|
||||||
const redirect = () => {
|
|
||||||
return {
|
|
||||||
pathname: "/import_users",
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
|
||||||
small: {
|
|
||||||
height: "40px",
|
|
||||||
width: "40px",
|
|
||||||
},
|
|
||||||
large: {
|
|
||||||
height: "120px",
|
|
||||||
width: "120px",
|
|
||||||
float: "right",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const choices_medium = [
|
const choices_medium = [
|
||||||
{ id: "email", name: "resources.users.email" },
|
{ id: "email", name: "resources.users.email" },
|
||||||
@@ -101,7 +82,6 @@ const UserListActions = ({
|
|||||||
filterValues,
|
filterValues,
|
||||||
permanentFilter,
|
permanentFilter,
|
||||||
hasCreate, // you can hide CreateButton if hasCreate = false
|
hasCreate, // you can hide CreateButton if hasCreate = false
|
||||||
basePath,
|
|
||||||
selectedIds,
|
selectedIds,
|
||||||
onUnselectItems,
|
onUnselectItems,
|
||||||
showFilter,
|
showFilter,
|
||||||
@@ -119,7 +99,7 @@ const UserListActions = ({
|
|||||||
filterValues,
|
filterValues,
|
||||||
context: "button",
|
context: "button",
|
||||||
})}
|
})}
|
||||||
<CreateButton basePath={basePath} />
|
<CreateButton />
|
||||||
<ExportButton
|
<ExportButton
|
||||||
disabled={total === 0}
|
disabled={total === 0}
|
||||||
resource={resource}
|
resource={resource}
|
||||||
@@ -129,8 +109,8 @@ const UserListActions = ({
|
|||||||
maxResults={maxResults}
|
maxResults={maxResults}
|
||||||
/>
|
/>
|
||||||
{/* Add your custom actions */}
|
{/* Add your custom actions */}
|
||||||
<Button component={Link} to={redirect} label="CSV Import">
|
<Button component={Link} to="/import_users" label="CSV Import">
|
||||||
<GetAppIcon style={{ transform: "rotate(180deg)", fontSize: "20" }} />
|
<GetAppIcon sx={{ transform: "rotate(180deg)", fontSize: "20px" }} />
|
||||||
</Button>
|
</Button>
|
||||||
</TopToolbar>
|
</TopToolbar>
|
||||||
);
|
);
|
||||||
@@ -169,12 +149,7 @@ const UserBulkActionButtons = props => (
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
const AvatarField = ({ source, className, record = {} }) => (
|
|
||||||
<Avatar src={record[source]} className={className} />
|
|
||||||
);
|
|
||||||
|
|
||||||
export const UserList = props => {
|
export const UserList = props => {
|
||||||
const classes = useStyles();
|
|
||||||
return (
|
return (
|
||||||
<List
|
<List
|
||||||
{...props}
|
{...props}
|
||||||
@@ -182,13 +157,12 @@ export const UserList = props => {
|
|||||||
filterDefaultValues={{ guests: true, deactivated: false }}
|
filterDefaultValues={{ guests: true, deactivated: false }}
|
||||||
sort={{ field: "name", order: "ASC" }}
|
sort={{ field: "name", order: "ASC" }}
|
||||||
actions={<UserListActions maxResults={10000} />}
|
actions={<UserListActions maxResults={10000} />}
|
||||||
bulkActionButtons={<UserBulkActionButtons />}
|
|
||||||
pagination={<UserPagination />}
|
pagination={<UserPagination />}
|
||||||
>
|
>
|
||||||
<Datagrid rowClick="edit">
|
<Datagrid rowClick="edit" bulkActionButtons={<UserBulkActionButtons />}>
|
||||||
<AvatarField
|
<AvatarField
|
||||||
source="avatar_src"
|
source="avatar_src"
|
||||||
className={classes.small}
|
sx={{ height: "40px", width: "40px" }}
|
||||||
sortBy="avatar_url"
|
sortBy="avatar_url"
|
||||||
/>
|
/>
|
||||||
<TextField source="id" sortBy="name" />
|
<TextField source="id" sortBy="name" />
|
||||||
@@ -262,7 +236,7 @@ export function generateRandomUser() {
|
|||||||
|
|
||||||
const UserEditToolbar = props => (
|
const UserEditToolbar = props => (
|
||||||
<Toolbar {...props}>
|
<Toolbar {...props}>
|
||||||
<SaveButton submitOnEnter={true} disabled={props.pristine} />
|
<SaveButton disabled={props.pristine} />
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -302,7 +276,6 @@ export const UserCreate = props => (
|
|||||||
source="user_type"
|
source="user_type"
|
||||||
choices={choices_type}
|
choices={choices_type}
|
||||||
translateChoice={false}
|
translateChoice={false}
|
||||||
allowEmpty={true}
|
|
||||||
resettable
|
resettable
|
||||||
/>
|
/>
|
||||||
<BooleanInput source="admin" />
|
<BooleanInput source="admin" />
|
||||||
@@ -344,7 +317,6 @@ const UserTitle = props => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const UserEdit = props => {
|
export const UserEdit = props => {
|
||||||
const classes = useStyles();
|
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Edit {...props} title={<UserTitle />} actions={<UserEditActions />}>
|
<Edit {...props} title={<UserTitle />} actions={<UserEditActions />}>
|
||||||
@@ -356,7 +328,7 @@ export const UserEdit = props => {
|
|||||||
<AvatarField
|
<AvatarField
|
||||||
source="avatar_src"
|
source="avatar_src"
|
||||||
sortable={false}
|
sortable={false}
|
||||||
className={classes.large}
|
sx={{ height: "120px", width: "120px", float: "right" }}
|
||||||
/>
|
/>
|
||||||
<TextInput source="id" disabled />
|
<TextInput source="id" disabled />
|
||||||
<TextInput source="displayname" />
|
<TextInput source="displayname" />
|
||||||
@@ -369,7 +341,6 @@ export const UserEdit = props => {
|
|||||||
source="user_type"
|
source="user_type"
|
||||||
choices={choices_type}
|
choices={choices_type}
|
||||||
translateChoice={false}
|
translateChoice={false}
|
||||||
allowEmpty={true}
|
|
||||||
resettable
|
resettable
|
||||||
/>
|
/>
|
||||||
<BooleanInput source="admin" />
|
<BooleanInput source="admin" />
|
||||||
@@ -513,7 +484,7 @@ export const UserEdit = props => {
|
|||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
rowClick={(id, basePath, record) => "/rooms/" + id + "/show"}
|
rowClick={(id, resource, record) => "/rooms/" + id + "/show"}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
source="id"
|
source="id"
|
||||||
|
|||||||
+385
@@ -0,0 +1,385 @@
|
|||||||
|
import italianMessages from "ra-language-italian";
|
||||||
|
|
||||||
|
const it = {
|
||||||
|
...italianMessages,
|
||||||
|
synapseadmin: {
|
||||||
|
auth: {
|
||||||
|
base_url: "URL dell'homeserver",
|
||||||
|
welcome: "Benvenuto in Synapse-admin",
|
||||||
|
server_version: "Versione di Synapse",
|
||||||
|
username_error:
|
||||||
|
"Per favore inserisci un ID utente completo: '@utente:dominio'",
|
||||||
|
protocol_error: "L'URL deve iniziare per 'http://' o 'https://'",
|
||||||
|
url_error: "URL del server Matrix non valido",
|
||||||
|
sso_sign_in: "Accedi con SSO",
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
invalid_user_id: "ID utente non valido su questo homeserver.",
|
||||||
|
tabs: { sso: "SSO" },
|
||||||
|
},
|
||||||
|
rooms: {
|
||||||
|
tabs: {
|
||||||
|
basic: "Semplice",
|
||||||
|
members: "Membro",
|
||||||
|
detail: "Dettagli",
|
||||||
|
permission: "Permessi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reports: { tabs: { basic: "Semplice", detail: "Dettagli" } },
|
||||||
|
},
|
||||||
|
import_users: {
|
||||||
|
error: {
|
||||||
|
at_entry: "Alla voce %{entry}: %{message}",
|
||||||
|
error: "Errore",
|
||||||
|
required_field: "Il campo '%{field}' non è presente",
|
||||||
|
invalid_value:
|
||||||
|
"Valore non valido alla riga %{row}. '%{field}' Il campo può essere solo 'true' o 'false'",
|
||||||
|
unreasonably_big:
|
||||||
|
"Impossibile caricare un file così grosso (%{size} megabyte)",
|
||||||
|
already_in_progress: "Un import è attualmente già in caricamento",
|
||||||
|
id_exits: "L'ID %{id} è già presente",
|
||||||
|
},
|
||||||
|
title: "Importa utenti tramite file CSV",
|
||||||
|
goToPdf: "Vai al PDF",
|
||||||
|
cards: {
|
||||||
|
importstats: {
|
||||||
|
header: "Importa utenti",
|
||||||
|
users_total:
|
||||||
|
"%{smart_count} utente nel file CSV |||| %{smart_count} utenti nel file CSV",
|
||||||
|
guest_count: "%{smart_count} ospite |||| %{smart_count} ospiti",
|
||||||
|
admin_count:
|
||||||
|
"%{smart_count} amministratore |||| %{smart_count} amministratori",
|
||||||
|
},
|
||||||
|
conflicts: {
|
||||||
|
header: "Strategia di conflitto",
|
||||||
|
mode: {
|
||||||
|
stop: "Stoppa al conflitto",
|
||||||
|
skip: "Mostra l'errore e ignora il conflitto",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ids: {
|
||||||
|
header: "ID",
|
||||||
|
all_ids_present: "ID presenti in ogni voce",
|
||||||
|
count_ids_present:
|
||||||
|
"%{smart_count} voce con ID |||| %{smart_count} voci con ID",
|
||||||
|
mode: {
|
||||||
|
ignore: "Ignora gli ID nel file CSV e creane di nuovi",
|
||||||
|
update: "Aggiorna le voci esistenti",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
passwords: {
|
||||||
|
header: "Passwords",
|
||||||
|
all_passwords_present: "Password presenti in ogni voce",
|
||||||
|
count_passwords_present:
|
||||||
|
"%{smart_count} voce con password |||| %{smart_count} voci con password",
|
||||||
|
use_passwords: "Usa le password dal file CSV",
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
header: "Input file CSV",
|
||||||
|
explanation:
|
||||||
|
"Qui puoi caricare un file con valori separati da virgole che verrà poi utilizzato per creare o aggiornare gli utenti. Il file deve includere i campi 'id' and 'displayname'. Puoi scaricare un file di esempio per adattarlo: ",
|
||||||
|
},
|
||||||
|
startImport: {
|
||||||
|
simulate_only: "Solo simulazione",
|
||||||
|
run_import: "Importa",
|
||||||
|
},
|
||||||
|
results: {
|
||||||
|
header: "Importa i risultati",
|
||||||
|
total:
|
||||||
|
"%{smart_count} voce in totale |||| %{smart_count} voci in totale",
|
||||||
|
successful: "%{smart_count} voci importate con successo",
|
||||||
|
skipped: "%{smart_count} voci ignorate",
|
||||||
|
download_skipped: "Scarica le voci ignorate",
|
||||||
|
with_error:
|
||||||
|
"%{smart_count} voce con errori ||| %{smart_count} voci con errori",
|
||||||
|
simulated_only: "Il processo era stato solamente simulato",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resources: {
|
||||||
|
users: {
|
||||||
|
name: "Utente |||| Utenti",
|
||||||
|
email: "Email",
|
||||||
|
msisdn: "Telefono",
|
||||||
|
threepid: "Email / Telefono",
|
||||||
|
fields: {
|
||||||
|
avatar: "Avatar",
|
||||||
|
id: "ID utente",
|
||||||
|
name: "Nome",
|
||||||
|
is_guest: "Ospite",
|
||||||
|
admin: "Amministratore",
|
||||||
|
deactivated: "Disattivato",
|
||||||
|
guests: "Mostra gli ospiti",
|
||||||
|
show_deactivated: "Mostra gli utenti disattivati",
|
||||||
|
user_id: "Cerca utente",
|
||||||
|
displayname: "Nickname",
|
||||||
|
password: "Password",
|
||||||
|
avatar_url: "URL dell'avatar",
|
||||||
|
avatar_src: "Avatar",
|
||||||
|
medium: "Medium",
|
||||||
|
threepids: "3PID",
|
||||||
|
address: "Indirizzo",
|
||||||
|
creation_ts_ms: "Creazione del timestamp",
|
||||||
|
consent_version: "Versione minima richiesta",
|
||||||
|
auth_provider: "Provider",
|
||||||
|
user_type: "Tipo d'utente",
|
||||||
|
},
|
||||||
|
helper: {
|
||||||
|
password:
|
||||||
|
"Cambiando la password l'utente verrà disconnesso da tutte le sessioni attive.",
|
||||||
|
deactivate: "Devi fornire una password per riattivare l'account.",
|
||||||
|
erase: "Constrassegna l'utente come cancellato dal GDPR",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
erase: "Cancella i dati dell'utente",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rooms: {
|
||||||
|
name: "Stanza |||| Stanze",
|
||||||
|
fields: {
|
||||||
|
room_id: "ID della stanza",
|
||||||
|
name: "Nome",
|
||||||
|
canonical_alias: "Alias",
|
||||||
|
joined_members: "Membri",
|
||||||
|
joined_local_members: "Membri locali",
|
||||||
|
joined_local_devices: "Dispositivi locali",
|
||||||
|
state_events: "Eventi di stato / Complessità",
|
||||||
|
version: "Versione",
|
||||||
|
is_encrypted: "Criptato",
|
||||||
|
encryption: "Crittografia",
|
||||||
|
federatable: "Federabile",
|
||||||
|
public: "Visibile nella cartella della stanza",
|
||||||
|
creator: "Creatore",
|
||||||
|
join_rules: "Regole per entrare",
|
||||||
|
guest_access: "Entra come ospite",
|
||||||
|
history_visibility: "Visibilità temporale",
|
||||||
|
topic: "Topic",
|
||||||
|
avatar: "Avatar",
|
||||||
|
},
|
||||||
|
helper: {
|
||||||
|
/* forward_extremities:
|
||||||
|
"Forward extremities are the leaf events at the end of a Directed acyclic graph (DAG) in a room, aka events that have no children. The more exist in a room, the more state resolution that Synapse needs to perform (hint: it's an expensive operation). While Synapse has code to prevent too many of these existing at one time in a room, bugs can sometimes make them crop up again. If a room has >10 forward extremities, it's worth checking which room is the culprit and potentially removing them using the SQL queries mentioned in #1760.", */
|
||||||
|
},
|
||||||
|
enums: {
|
||||||
|
join_rules: {
|
||||||
|
public: "Pubblica",
|
||||||
|
knock: "Bussa",
|
||||||
|
invite: "Invita",
|
||||||
|
private: "Privata",
|
||||||
|
},
|
||||||
|
guest_access: {
|
||||||
|
can_join: "Gli utenti ospiti possono entrare",
|
||||||
|
forbidden: "Gli utenti ospiti non possono entrare",
|
||||||
|
},
|
||||||
|
history_visibility: {
|
||||||
|
invited: "Dall'invito",
|
||||||
|
joined: "Dall'entrata",
|
||||||
|
shared: "Dalla condivisione",
|
||||||
|
world_readable: "Chiunque",
|
||||||
|
},
|
||||||
|
unencrypted: "Non criptata",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
erase: {
|
||||||
|
title: "Cancella stanza",
|
||||||
|
content:
|
||||||
|
"Sei sicuro di voler eliminare questa stanza? Questa azione è definitiva. Tutti i messaggi e i media condivisi in questa stanza verranno eliminati dal server!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reports: {
|
||||||
|
name: "Evento segnalato |||| Eventi segnalati",
|
||||||
|
fields: {
|
||||||
|
id: "ID",
|
||||||
|
received_ts: "Orario del report",
|
||||||
|
user_id: "richiedente",
|
||||||
|
name: "nome della stanza",
|
||||||
|
score: "punteggio",
|
||||||
|
reason: "ragione",
|
||||||
|
event_id: "ID dell'evento",
|
||||||
|
event_json: {
|
||||||
|
origin: "server di origine",
|
||||||
|
origin_server_ts: "ora dell'invio",
|
||||||
|
type: "tipo di evento",
|
||||||
|
content: {
|
||||||
|
msgtype: "tipo di contenuto",
|
||||||
|
body: "contenuto",
|
||||||
|
format: "formato",
|
||||||
|
formatted_body: "contenuto formattato",
|
||||||
|
algorithm: "algoritmo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
connections: {
|
||||||
|
name: "Connessioni",
|
||||||
|
fields: {
|
||||||
|
last_seen: "Data",
|
||||||
|
ip: "Indirizzo IP",
|
||||||
|
user_agent: "agente utente",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
devices: {
|
||||||
|
name: "Dispositivo |||| Dispositivi",
|
||||||
|
fields: {
|
||||||
|
device_id: "ID del dispositivo",
|
||||||
|
display_name: "Nome del dispositivo",
|
||||||
|
last_seen_ts: "Timestamp",
|
||||||
|
last_seen_ip: "Indirizzo IP",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
erase: {
|
||||||
|
title: "Rimozione del dispositivo %{id}",
|
||||||
|
content: 'Sei sicuro di voler rimuovere il dispositivo "%{name}"?',
|
||||||
|
success: "Dispositivo rimosso con successo.",
|
||||||
|
failure: "C'è stato un errore.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users_media: {
|
||||||
|
name: "Media",
|
||||||
|
fields: {
|
||||||
|
media_id: "ID del media",
|
||||||
|
media_length: "Peso del file (in Byte)",
|
||||||
|
media_type: "Tipo",
|
||||||
|
upload_name: "Nome del file",
|
||||||
|
quarantined_by: "In quarantena da",
|
||||||
|
safe_from_quarantine: "Protetto dalla quarantena",
|
||||||
|
created_ts: "Creato",
|
||||||
|
last_access_ts: "Ultimo accesso",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete_media: {
|
||||||
|
name: "Media",
|
||||||
|
fields: {
|
||||||
|
before_ts: "ultimo accesso effettuato prima",
|
||||||
|
size_gt: "Più grande di (in byte)",
|
||||||
|
keep_profiles: "Mantieni le immagini del profilo",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
send: "Cancella media",
|
||||||
|
send_success: "Richiesta inviata con successo.",
|
||||||
|
send_failure: "C'è stato un errore.",
|
||||||
|
},
|
||||||
|
helper: {
|
||||||
|
send: "Questa API cancella i media locali dal disco del tuo server. Questo include anche ogni miniatura e copia del media scaricato. Questa API non inciderà sui media che sono stati caricati nei repository esterni.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
protect_media: {
|
||||||
|
action: {
|
||||||
|
create: "Non protetto, proteggi",
|
||||||
|
delete: "Protetto, rimuovi protezione",
|
||||||
|
none: "In quarantena",
|
||||||
|
send_success: "Stato della protezione cambiato con successo.",
|
||||||
|
send_failure: "C'è stato un errore.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
quarantine_media: {
|
||||||
|
action: {
|
||||||
|
name: "Quarantina",
|
||||||
|
create: "Aggiungi alla quarantena",
|
||||||
|
delete: "In quarantena, rimuovi dalla quarantena",
|
||||||
|
none: "Protetto dalla quarantena",
|
||||||
|
send_success: "Stato della quarantena cambiato con successo.",
|
||||||
|
send_failure: "C'è stato un errore.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pushers: {
|
||||||
|
name: "Pusher |||| Pusher",
|
||||||
|
fields: {
|
||||||
|
app: "App",
|
||||||
|
app_display_name: "Nome dell'app",
|
||||||
|
app_id: "ID dell'app",
|
||||||
|
device_display_name: "Nome del dispositivo",
|
||||||
|
kind: "Tipo",
|
||||||
|
lang: "Lingua",
|
||||||
|
profile_tag: "Tag del profilo",
|
||||||
|
pushkey: "Pushkey",
|
||||||
|
data: { url: "URL" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
servernotices: {
|
||||||
|
name: "Avvisi del server",
|
||||||
|
send: "Invia avvisi",
|
||||||
|
fields: {
|
||||||
|
body: "Messaggio",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
send: "Invia nota",
|
||||||
|
send_success: "Avviso inviato con successo.",
|
||||||
|
send_failure: "C'è stato un errore.",
|
||||||
|
},
|
||||||
|
helper: {
|
||||||
|
send: 'Invia un avviso dal server agli utenti selezionati. La feature "Avvisi del server" è stata attivata sul server.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user_media_statistics: {
|
||||||
|
name: "Media degli utenti",
|
||||||
|
fields: {
|
||||||
|
media_count: "Numero media",
|
||||||
|
media_length: "Lunghezza media",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
forward_extremities: {
|
||||||
|
name: "Invia estremità",
|
||||||
|
fields: {
|
||||||
|
id: "Event ID",
|
||||||
|
received_ts: "Timestamp",
|
||||||
|
depth: "Profondità",
|
||||||
|
state_group: "State group",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
room_state: {
|
||||||
|
name: "Eventi di stato",
|
||||||
|
fields: {
|
||||||
|
type: "Tipo",
|
||||||
|
content: "Contenuto",
|
||||||
|
origin_server_ts: "Ora dell'invio",
|
||||||
|
sender: "Mittente",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
room_directory: {
|
||||||
|
name: "Elenco delle stanze",
|
||||||
|
fields: {
|
||||||
|
world_readable: "gli utenti ospite possono vedere senza entrare",
|
||||||
|
guest_can_join: "gli utenti ospite possono entrare",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
title:
|
||||||
|
"Cancella stanza dall'elenco |||| Cancella %{smart_count} stanze dall'elenco",
|
||||||
|
content:
|
||||||
|
"Sei sicuro di voler rimuovere questa stanza dall'elenco? |||| Sei sicuro di voler rimuovere %{smart_count} stanze dall'elenco?",
|
||||||
|
erase: "Rimuovi dall'elenco",
|
||||||
|
create: "Crea",
|
||||||
|
send_success: "Stanza creata con successo.",
|
||||||
|
send_failure: "C'è stato un errore.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
destinations: {
|
||||||
|
name: "Federazione",
|
||||||
|
fields: {
|
||||||
|
destination: "Destinazione",
|
||||||
|
failure_ts: "Timestamp dell'errore",
|
||||||
|
retry_last_ts: "Tentativo ultimo timestamp",
|
||||||
|
retry_interval: "Intervallo dei tentativi",
|
||||||
|
last_successful_stream_ordering: "Ultimo flusso riuscito con successo",
|
||||||
|
stream_ordering: "Flusso",
|
||||||
|
},
|
||||||
|
action: { reconnect: "Riconnetti" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
registration_tokens: {
|
||||||
|
name: "Token di registrazione",
|
||||||
|
fields: {
|
||||||
|
token: "Token",
|
||||||
|
valid: "Token valido",
|
||||||
|
uses_allowed: "Usi permessi",
|
||||||
|
pending: "In attesa",
|
||||||
|
completed: "Completato",
|
||||||
|
expiry_time: "Data della scadenza",
|
||||||
|
length: "Lunghezza",
|
||||||
|
},
|
||||||
|
helper: { length: "Lunghezza del token se non viene dato alcun token." },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default it;
|
||||||
Reference in New Issue
Block a user