Merge tag '0.8.0' into amp.chat
Change-Id: I2e362a911083149c82a8c11b6c4594bb4c760a33
This commit is contained in:
commit
7b5c0e2845
23
README.md
23
README.md
@ -4,7 +4,7 @@
|
||||
|
||||
This project is built using [react-admin](https://marmelab.com/react-admin/).
|
||||
|
||||
It needs at least Synapse v1.23.0 for all functions to work as expected!
|
||||
It needs at least Synapse v1.27.0 for all functions to work as expected!
|
||||
|
||||
You get your server version with the request `/_synapse/admin/v1/server_version`.
|
||||
See also [Synapse version API](https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/version_api.rst).
|
||||
@ -35,7 +35,26 @@ Steps for 1):
|
||||
|
||||
Steps for 2):
|
||||
|
||||
- run the Docker container: `docker run -p 8080:80 awesometechnologies/synapse-admin`
|
||||
- run the Docker container from the public docker registry: `docker run -p 8080:80 awesometechnologies/synapse-admin` or use the (docker-compose.yml)[docker-compose.yml]: `docker-compose up -d`
|
||||
|
||||
> note: if you're building on an architecture other than amd64 (for example a raspberry pi), make sure to define a maximum ram for node. otherwise the build will fail.
|
||||
|
||||
```yml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
synapse-admin:
|
||||
container_name: synapse-admin
|
||||
hostname: synapse-admin
|
||||
build:
|
||||
context: https://github.com/Awesome-Technologies/synapse-admin.git
|
||||
# args:
|
||||
# - NODE_OPTIONS="--max_old_space_size=1024"
|
||||
ports:
|
||||
- "8080:80"
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
- browse to http://localhost:8080
|
||||
|
||||
## Screenshots
|
||||
|
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
@ -0,0 +1,21 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
synapse-admin:
|
||||
container_name: synapse-admin
|
||||
hostname: synapse-admin
|
||||
image: awesometechnologies/synapse-admin:latest
|
||||
# build:
|
||||
# context: .
|
||||
|
||||
# to use the docker-compose as standalone without a local repo clone,
|
||||
# replace the context definition with this:
|
||||
# context: https://github.com/Awesome-Technologies/synapse-admin.git
|
||||
|
||||
# if you're building on an architecture other than amd64, make sure
|
||||
# to define a maximum ram for node. otherwise the build will fail.
|
||||
# args:
|
||||
# - NODE_OPTIONS="--max_old_space_size=1024"
|
||||
ports:
|
||||
- "8080:80"
|
||||
restart: unless-stopped
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "synapse-admin",
|
||||
"version": "AMP/2020.08",
|
||||
"version": "AMP/2021.05",
|
||||
"description": "Admin GUI for the Matrix.org server Synapse",
|
||||
"author": "Awesome Technologies Innovationslabor GmbH",
|
||||
"license": "Apache-2.0",
|
||||
@ -19,7 +19,8 @@
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"prettier": "^2.0.0"
|
||||
"prettier": "^2.0.0",
|
||||
"ra-test": "^3.14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@progress/kendo-drawing": "^1.6.0",
|
||||
@ -28,9 +29,10 @@
|
||||
"papaparse": "^5.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"qrcode.react": "^1.0.0",
|
||||
"ra-language-chinese": "^2.0.10",
|
||||
"ra-language-german": "^2.1.2",
|
||||
"react": "^16.13.1",
|
||||
"react-admin": "^3.10.0",
|
||||
"react": "^17.0.0",
|
||||
"react-admin": "^3.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-scripts": "^3.4.4"
|
||||
},
|
||||
|
11
src/App.js
11
src/App.js
@ -12,15 +12,20 @@ import EqualizerIcon from "@material-ui/icons/Equalizer";
|
||||
import { UserMediaStatsList } from "./components/statistics";
|
||||
import RoomIcon from "@material-ui/icons/ViewList";
|
||||
import ReportIcon from "@material-ui/icons/Warning";
|
||||
import FolderSharedIcon from "@material-ui/icons/FolderShared";
|
||||
import { ImportFeature } from "./components/ImportFeature";
|
||||
import { RoomDirectoryList } from "./components/RoomDirectory";
|
||||
import { Route } from "react-router-dom";
|
||||
import germanMessages from "./i18n/de";
|
||||
import englishMessages from "./i18n/en";
|
||||
import chineseMessages from "./i18n/zh";
|
||||
import ShowUserPdf from "./components/ShowUserPdf";
|
||||
|
||||
// TODO: Can we use lazy loading together with browser locale?
|
||||
const messages = {
|
||||
de: germanMessages,
|
||||
en: englishMessages,
|
||||
zh: chineseMessages,
|
||||
};
|
||||
const i18nProvider = polyglotI18nProvider(
|
||||
locale => (messages[locale] ? messages[locale] : messages.en),
|
||||
@ -64,6 +69,11 @@ const App = () => (
|
||||
show={ReportShow}
|
||||
icon={ReportIcon}
|
||||
/>
|
||||
<Resource
|
||||
name="room_directory"
|
||||
list={RoomDirectoryList}
|
||||
icon={FolderSharedIcon}
|
||||
/>
|
||||
<Resource name="connections" />
|
||||
<Resource name="devices" />
|
||||
<Resource name="room_members" />
|
||||
@ -71,6 +81,7 @@ const App = () => (
|
||||
<Resource name="joined_rooms" />
|
||||
<Resource name="pushers" />
|
||||
<Resource name="servernotices" />
|
||||
<Resource name="room_state" />
|
||||
</Admin>
|
||||
);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { TestContext } from "react-admin";
|
||||
import { TestContext } from "ra-test";
|
||||
import { shallow } from "enzyme";
|
||||
import App from "./App";
|
||||
|
||||
|
@ -255,6 +255,7 @@ const LoginPage = ({ theme }) => {
|
||||
>
|
||||
<MenuItem value="de">Deutsch</MenuItem>
|
||||
<MenuItem value="en">English</MenuItem>
|
||||
<MenuItem value="zh">简体中文</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
<FormDataConsumer>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { TestContext } from "react-admin";
|
||||
import { TestContext } from "ra-test";
|
||||
import { shallow } from "enzyme";
|
||||
import LoginPage from "./LoginPage";
|
||||
|
||||
|
252
src/components/RoomDirectory.js
Normal file
252
src/components/RoomDirectory.js
Normal file
@ -0,0 +1,252 @@
|
||||
import React, { Fragment } from "react";
|
||||
import Avatar from "@material-ui/core/Avatar";
|
||||
import { Chip } from "@material-ui/core";
|
||||
import { connect } from "react-redux";
|
||||
import FolderSharedIcon from "@material-ui/icons/FolderShared";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
BooleanField,
|
||||
BulkDeleteButton,
|
||||
Button,
|
||||
Datagrid,
|
||||
DeleteButton,
|
||||
Filter,
|
||||
List,
|
||||
NumberField,
|
||||
Pagination,
|
||||
TextField,
|
||||
useCreate,
|
||||
useMutation,
|
||||
useNotify,
|
||||
useTranslate,
|
||||
useRefresh,
|
||||
useUnselectAll,
|
||||
} from "react-admin";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
small: {
|
||||
height: "40px",
|
||||
width: "40px",
|
||||
},
|
||||
});
|
||||
|
||||
const RoomDirectoryPagination = props => (
|
||||
<Pagination {...props} rowsPerPageOptions={[100, 500, 1000, 2000]} />
|
||||
);
|
||||
|
||||
export const RoomDirectoryDeleteButton = props => {
|
||||
const translate = useTranslate();
|
||||
|
||||
return (
|
||||
<DeleteButton
|
||||
{...props}
|
||||
label="resources.room_directory.action.erase"
|
||||
redirect={false}
|
||||
mutationMode="pessimistic"
|
||||
confirmTitle={translate("resources.room_directory.action.title", {
|
||||
smart_count: 1,
|
||||
})}
|
||||
confirmContent={translate("resources.room_directory.action.content", {
|
||||
smart_count: 1,
|
||||
})}
|
||||
resource="room_directory"
|
||||
icon={<FolderSharedIcon />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const RoomDirectoryBulkDeleteButton = props => (
|
||||
<BulkDeleteButton
|
||||
{...props}
|
||||
label="resources.room_directory.action.erase"
|
||||
undoable={false}
|
||||
confirmTitle="resources.room_directory.action.title"
|
||||
confirmContent="resources.room_directory.action.content"
|
||||
resource="room_directory"
|
||||
icon={<FolderSharedIcon />}
|
||||
/>
|
||||
);
|
||||
|
||||
export const RoomDirectoryBulkSaveButton = ({ selectedIds }) => {
|
||||
const notify = useNotify();
|
||||
const refresh = useRefresh();
|
||||
const unselectAll = useUnselectAll();
|
||||
const [createMany, { loading }] = useMutation();
|
||||
|
||||
const handleSend = values => {
|
||||
createMany(
|
||||
{
|
||||
type: "createMany",
|
||||
resource: "room_directory",
|
||||
payload: { ids: selectedIds, data: {} },
|
||||
},
|
||||
{
|
||||
onSuccess: ({ data }) => {
|
||||
notify("resources.room_directory.action.send_success");
|
||||
unselectAll("rooms");
|
||||
refresh();
|
||||
},
|
||||
onFailure: error =>
|
||||
notify("resources.room_directory.action.send_failure", "error"),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
label="resources.room_directory.action.create"
|
||||
onClick={handleSend}
|
||||
disabled={loading}
|
||||
>
|
||||
<FolderSharedIcon />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export const RoomDirectorySaveButton = ({ record }) => {
|
||||
const notify = useNotify();
|
||||
const refresh = useRefresh();
|
||||
const [create, { loading }] = useCreate("room_directory");
|
||||
|
||||
const handleSend = values => {
|
||||
create(
|
||||
{
|
||||
payload: { data: { id: record.id } },
|
||||
},
|
||||
{
|
||||
onSuccess: ({ data }) => {
|
||||
notify("resources.room_directory.action.send_success");
|
||||
refresh();
|
||||
},
|
||||
onFailure: error =>
|
||||
notify("resources.room_directory.action.send_failure", "error"),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
label="resources.room_directory.action.create"
|
||||
onClick={handleSend}
|
||||
disabled={loading}
|
||||
>
|
||||
<FolderSharedIcon />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const RoomDirectoryBulkActionButtons = props => (
|
||||
<Fragment>
|
||||
<RoomDirectoryBulkDeleteButton {...props} />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
const AvatarField = ({ source, className, record = {} }) => (
|
||||
<Avatar src={record[source]} className={className} />
|
||||
);
|
||||
|
||||
const RoomDirectoryFilter = ({ ...props }) => {
|
||||
const translate = useTranslate();
|
||||
return (
|
||||
<Filter {...props}>
|
||||
<Chip
|
||||
label={translate("resources.rooms.fields.room_id")}
|
||||
source="room_id"
|
||||
defaultValue={false}
|
||||
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 = ({ ...props }) => {
|
||||
const classes = useStyles();
|
||||
const translate = useTranslate();
|
||||
const filter = props.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 />}
|
||||
filters={<RoomDirectoryFilter />}
|
||||
perPage={100}
|
||||
>
|
||||
<Datagrid>
|
||||
<AvatarField
|
||||
source="avatar_src"
|
||||
sortable={false}
|
||||
className={classes.small}
|
||||
label={translate("resources.rooms.fields.avatar")}
|
||||
/>
|
||||
<TextField
|
||||
source="name"
|
||||
sortable={false}
|
||||
label={translate("resources.rooms.fields.name")}
|
||||
/>
|
||||
{roomIdFilter && (
|
||||
<TextField
|
||||
source="room_id"
|
||||
sortable={false}
|
||||
label={translate("resources.rooms.fields.room_id")}
|
||||
/>
|
||||
)}
|
||||
{canonicalAliasFilter && (
|
||||
<TextField
|
||||
source="canonical_alias"
|
||||
sortable={false}
|
||||
label={translate("resources.rooms.fields.canonical_alias")}
|
||||
/>
|
||||
)}
|
||||
{topicFilter && (
|
||||
<TextField
|
||||
source="topic"
|
||||
sortable={false}
|
||||
label={translate("resources.rooms.fields.topic")}
|
||||
/>
|
||||
)}
|
||||
<NumberField
|
||||
source="num_joined_members"
|
||||
sortable={false}
|
||||
label={translate("resources.rooms.fields.joined_members")}
|
||||
/>
|
||||
<BooleanField
|
||||
source="world_readable"
|
||||
sortable={false}
|
||||
label={translate("resources.room_directory.fields.world_readable")}
|
||||
/>
|
||||
<BooleanField
|
||||
source="guest_can_join"
|
||||
sortable={false}
|
||||
label={translate("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
|
||||
);
|
145
src/components/media.js
Normal file
145
src/components/media.js
Normal file
@ -0,0 +1,145 @@
|
||||
import React, { Fragment, useState } from "react";
|
||||
import classnames from "classnames";
|
||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
BooleanInput,
|
||||
Button,
|
||||
DateTimeInput,
|
||||
NumberInput,
|
||||
SaveButton,
|
||||
SimpleForm,
|
||||
Toolbar,
|
||||
useDelete,
|
||||
useNotify,
|
||||
useTranslate,
|
||||
} from "react-admin";
|
||||
import IconCancel from "@material-ui/icons/Cancel";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
import DeleteSweepIcon from "@material-ui/icons/DeleteSweep";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
deleteButton: {
|
||||
color: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: fade(theme.palette.error.main, 0.12),
|
||||
// Reset on mouse devices
|
||||
"@media (hover: none)": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ name: "RaDeleteDeviceButton" }
|
||||
);
|
||||
|
||||
const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
||||
const translate = useTranslate();
|
||||
|
||||
const dateParser = v => {
|
||||
const d = new Date(v);
|
||||
if (isNaN(d)) return 0;
|
||||
return d.getTime();
|
||||
};
|
||||
|
||||
const DeleteMediaToolbar = props => {
|
||||
return (
|
||||
<Toolbar {...props}>
|
||||
<SaveButton
|
||||
label="resources.delete_media.action.send"
|
||||
icon={<DeleteSweepIcon />}
|
||||
/>
|
||||
<Button label="ra.action.cancel" onClick={onClose}>
|
||||
<IconCancel />
|
||||
</Button>
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} loading={loading}>
|
||||
<DialogTitle>
|
||||
{translate("resources.delete_media.action.send")}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
{translate("resources.delete_media.helper.send")}
|
||||
</DialogContentText>
|
||||
<SimpleForm
|
||||
toolbar={<DeleteMediaToolbar />}
|
||||
submitOnEnter={false}
|
||||
redirect={false}
|
||||
save={onSend}
|
||||
>
|
||||
<DateTimeInput
|
||||
fullWidth
|
||||
source="before_ts"
|
||||
label="resources.delete_media.fields.before_ts"
|
||||
defaultValue={0}
|
||||
parse={dateParser}
|
||||
/>
|
||||
<NumberInput
|
||||
fullWidth
|
||||
source="size_gt"
|
||||
label="resources.delete_media.fields.size_gt"
|
||||
defaultValue={0}
|
||||
min={0}
|
||||
step={1024}
|
||||
/>
|
||||
<BooleanInput
|
||||
fullWidth
|
||||
source="keep_profiles"
|
||||
label="resources.delete_media.fields.keep_profiles"
|
||||
defaultValue={true}
|
||||
/>
|
||||
</SimpleForm>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const DeleteMediaButton = props => {
|
||||
const classes = useStyles(props);
|
||||
const [open, setOpen] = useState(false);
|
||||
const notify = useNotify();
|
||||
const [deleteOne, { loading }] = useDelete("delete_media");
|
||||
|
||||
const handleDialogOpen = () => setOpen(true);
|
||||
const handleDialogClose = () => setOpen(false);
|
||||
|
||||
const handleSend = values => {
|
||||
deleteOne(
|
||||
{ payload: { ...values } },
|
||||
{
|
||||
onSuccess: () => {
|
||||
notify("resources.delete_media.action.send_success");
|
||||
handleDialogClose();
|
||||
},
|
||||
onFailure: () =>
|
||||
notify("resources.delete_media.action.send_failure", "error"),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Button
|
||||
label="resources.delete_media.action.send"
|
||||
onClick={handleDialogOpen}
|
||||
disabled={loading}
|
||||
className={classnames("ra-delete-button", classes.deleteButton)}
|
||||
>
|
||||
<DeleteSweepIcon />
|
||||
</Button>
|
||||
<DeleteMediaDialog
|
||||
open={open}
|
||||
onClose={handleDialogClose}
|
||||
onSend={handleSend}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
@ -6,11 +6,12 @@ import {
|
||||
AutocompleteInput,
|
||||
BooleanInput,
|
||||
BooleanField,
|
||||
BulkDeleteWithConfirmButton,
|
||||
BulkDeleteButton,
|
||||
Button,
|
||||
Create,
|
||||
Edit,
|
||||
Datagrid,
|
||||
DateField,
|
||||
DeleteButton,
|
||||
Filter,
|
||||
FormTab,
|
||||
@ -53,6 +54,13 @@ import UserIcon from "@material-ui/icons/Group";
|
||||
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||
import VisibilityIcon from "@material-ui/icons/Visibility";
|
||||
import ContentSave from "@material-ui/icons/Save";
|
||||
import EventIcon from "@material-ui/icons/Event";
|
||||
import {
|
||||
RoomDirectoryBulkDeleteButton,
|
||||
RoomDirectoryBulkSaveButton,
|
||||
RoomDirectoryDeleteButton,
|
||||
RoomDirectorySaveButton,
|
||||
} from "./RoomDirectory";
|
||||
|
||||
const RoomPagination = props => (
|
||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||
@ -447,16 +455,26 @@ export const RoomEdit = props => {
|
||||
};
|
||||
|
||||
const RoomShowActions = ({ basePath, data, resource }) => {
|
||||
const translate = useTranslate();
|
||||
var roomDirectoryStatus = "";
|
||||
if (data) {
|
||||
roomDirectoryStatus = data.public;
|
||||
}
|
||||
|
||||
return (
|
||||
<TopToolbar>
|
||||
{roomDirectoryStatus === false && (
|
||||
<RoomDirectorySaveButton record={data} />
|
||||
)}
|
||||
{roomDirectoryStatus === true && (
|
||||
<RoomDirectoryDeleteButton record={data} />
|
||||
)}
|
||||
<DeleteButton
|
||||
basePath={basePath}
|
||||
record={data}
|
||||
resource={resource}
|
||||
undoable={false}
|
||||
confirmTitle={translate("synapseadmin.rooms.delete.title")}
|
||||
confirmContent={translate("synapseadmin.rooms.delete.message")}
|
||||
mutationMode="pessimistic"
|
||||
confirmTitle="resources.rooms.action.erase.title"
|
||||
confirmContent="resources.rooms.action.erase.content"
|
||||
/>
|
||||
</TopToolbar>
|
||||
);
|
||||
@ -471,7 +489,9 @@ export const RoomShow = props => {
|
||||
<TextField source="room_id" />
|
||||
<TextField source="name" />
|
||||
<TextField source="canonical_alias" />
|
||||
<TextField source="creator" />
|
||||
<ReferenceField source="creator" reference="users">
|
||||
<TextField source="id" />
|
||||
</ReferenceField>
|
||||
</Tab>
|
||||
|
||||
<Tab
|
||||
@ -481,6 +501,7 @@ export const RoomShow = props => {
|
||||
>
|
||||
<TextField source="joined_members" />
|
||||
<TextField source="joined_local_members" />
|
||||
<TextField source="joined_local_devices" />
|
||||
<TextField source="state_events" />
|
||||
<TextField source="version" />
|
||||
<TextField
|
||||
@ -571,6 +592,42 @@ export const RoomShow = props => {
|
||||
]}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
label={translate("resources.room_state.name", { smart_count: 2 })}
|
||||
icon={<EventIcon />}
|
||||
path="state"
|
||||
>
|
||||
<ReferenceManyField
|
||||
reference="room_state"
|
||||
target="room_id"
|
||||
addLabel={false}
|
||||
>
|
||||
<Datagrid style={{ width: "100%" }}>
|
||||
<TextField source="type" sortable={false} />
|
||||
<DateField
|
||||
source="origin_server_ts"
|
||||
showTime
|
||||
options={{
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}}
|
||||
sortable={false}
|
||||
/>
|
||||
<TextField source="content" sortable={false} />
|
||||
<ReferenceField
|
||||
source="sender"
|
||||
reference="users"
|
||||
sortable={false}
|
||||
>
|
||||
<TextField source="id" />
|
||||
</ReferenceField>
|
||||
</Datagrid>
|
||||
</ReferenceManyField>
|
||||
</Tab>
|
||||
</TabbedShowLayout>
|
||||
</Show>
|
||||
);
|
||||
@ -578,7 +635,14 @@ export const RoomShow = props => {
|
||||
|
||||
const RoomBulkActionButtons = props => (
|
||||
<Fragment>
|
||||
<BulkDeleteWithConfirmButton {...props} />
|
||||
<RoomDirectoryBulkSaveButton {...props} />
|
||||
<RoomDirectoryBulkDeleteButton {...props} />
|
||||
<BulkDeleteButton
|
||||
{...props}
|
||||
confirmTitle="resources.rooms.action.erase.title"
|
||||
confirmContent="resources.rooms.action.erase.content"
|
||||
undoable={false}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@ -622,7 +686,6 @@ const FilterableRoomList = ({ ...props }) => {
|
||||
const stateEventsFilter = filter && filter.state_events ? true : false;
|
||||
const versionFilter = filter && filter.version ? true : false;
|
||||
const federateableFilter = filter && filter.federatable ? true : false;
|
||||
const translate = useTranslate();
|
||||
|
||||
return (
|
||||
<List
|
||||
@ -630,12 +693,7 @@ const FilterableRoomList = ({ ...props }) => {
|
||||
pagination={<RoomPagination />}
|
||||
sort={{ field: "name", order: "ASC" }}
|
||||
filters={<RoomFilter />}
|
||||
bulkActionButtons={
|
||||
<RoomBulkActionButtons
|
||||
confirmTitle={translate("synapseadmin.rooms.delete.title")}
|
||||
confirmContent={translate("synapseadmin.rooms.delete.message")}
|
||||
/>
|
||||
}
|
||||
bulkActionButtons={<RoomBulkActionButtons />}
|
||||
>
|
||||
<Datagrid rowClick="show">
|
||||
<EncryptionField
|
||||
|
@ -1,13 +1,51 @@
|
||||
import React from "react";
|
||||
import { cloneElement } from "react";
|
||||
import {
|
||||
Datagrid,
|
||||
ExportButton,
|
||||
Filter,
|
||||
List,
|
||||
NumberField,
|
||||
TextField,
|
||||
SearchInput,
|
||||
Pagination,
|
||||
sanitizeListRestProps,
|
||||
SearchInput,
|
||||
TextField,
|
||||
TopToolbar,
|
||||
useListContext,
|
||||
} from "react-admin";
|
||||
import { DeleteMediaButton } from "./media";
|
||||
|
||||
const ListActions = props => {
|
||||
const { className, exporter, filters, maxResults, ...rest } = props;
|
||||
const {
|
||||
currentSort,
|
||||
resource,
|
||||
displayedFilters,
|
||||
filterValues,
|
||||
showFilter,
|
||||
total,
|
||||
} = useListContext();
|
||||
return (
|
||||
<TopToolbar className={className} {...sanitizeListRestProps(rest)}>
|
||||
{filters &&
|
||||
cloneElement(filters, {
|
||||
resource,
|
||||
showFilter,
|
||||
displayedFilters,
|
||||
filterValues,
|
||||
context: "button",
|
||||
})}
|
||||
<DeleteMediaButton />
|
||||
<ExportButton
|
||||
disabled={total === 0}
|
||||
resource={resource}
|
||||
sort={currentSort}
|
||||
filterValues={filterValues}
|
||||
maxResults={maxResults}
|
||||
/>
|
||||
</TopToolbar>
|
||||
);
|
||||
};
|
||||
|
||||
const UserMediaStatsPagination = props => (
|
||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||
@ -23,6 +61,7 @@ export const UserMediaStatsList = props => {
|
||||
return (
|
||||
<List
|
||||
{...props}
|
||||
actions={<ListActions />}
|
||||
filters={<UserMediaStatsFilter />}
|
||||
pagination={<UserMediaStatsPagination />}
|
||||
sort={{ field: "media_length", order: "DESC" }}
|
||||
|
@ -126,19 +126,17 @@ const UserFilter = props => (
|
||||
</Filter>
|
||||
);
|
||||
|
||||
const UserBulkActionButtons = props => {
|
||||
const translate = useTranslate();
|
||||
return (
|
||||
<Fragment>
|
||||
<ServerNoticeBulkButton {...props} />
|
||||
<BulkDeleteButton
|
||||
{...props}
|
||||
label="resources.users.action.erase"
|
||||
title={translate("resources.users.helper.erase")}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
const UserBulkActionButtons = props => (
|
||||
<Fragment>
|
||||
<ServerNoticeBulkButton {...props} />
|
||||
<BulkDeleteButton
|
||||
{...props}
|
||||
label="resources.users.action.erase"
|
||||
confirmTitle="resources.users.helper.erase"
|
||||
undoable={false}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
const AvatarField = ({ source, className, record = {} }) => (
|
||||
<Avatar src={record[source]} className={className} />
|
||||
@ -271,7 +269,10 @@ const UserEditToolbar = props => {
|
||||
/>
|
||||
<DeleteButton
|
||||
label="resources.users.action.erase"
|
||||
title={translate("resources.users.helper.erase")}
|
||||
confirmTitle={translate("resources.users.helper.erase", {
|
||||
smart_count: 1,
|
||||
})}
|
||||
mutationMode="pessimistic"
|
||||
/>
|
||||
<ServerNoticeButton />
|
||||
</Toolbar>
|
||||
@ -504,7 +505,7 @@ export const UserEdit = props => {
|
||||
<TextField source="upload_name" sortable={false} />
|
||||
<TextField source="quarantined_by" sortable={false} />
|
||||
<BooleanField source="safe_from_quarantine" sortable={false} />
|
||||
<DeleteButton undoable={false} redirect={false} />
|
||||
<DeleteButton mutationMode="pessimistic" redirect={false} />
|
||||
</Datagrid>
|
||||
</ReferenceManyField>
|
||||
</FormTab>
|
||||
|
@ -36,11 +36,6 @@ export default {
|
||||
detail: "Details",
|
||||
permission: "Berechtigungen",
|
||||
},
|
||||
delete: {
|
||||
title: "Raum löschen",
|
||||
message:
|
||||
"Sind Sie sicher dass Sie den Raum löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden. Alle Nachrichten und Medien, die der Raum beinhaltet werden vom Server gelöscht!",
|
||||
},
|
||||
},
|
||||
reports: { tabs: { basic: "Allgemein", detail: "Details" } },
|
||||
},
|
||||
@ -175,16 +170,19 @@ export default {
|
||||
invite_members: "Mitglieder einladen",
|
||||
invitees: "Einladungen",
|
||||
joined_local_members: "Lokale Mitglieder",
|
||||
state_events: "Ereignisse",
|
||||
joined_local_devices: "Lokale Endgeräte",
|
||||
state_events: "Zustandsereignisse / Komplexität",
|
||||
version: "Version",
|
||||
is_encrypted: "Verschlüsselt",
|
||||
encryption: "Verschlüsselungs-Algorithmus",
|
||||
federatable: "Föderierbar",
|
||||
public: "Öffentlich",
|
||||
public: "Sichtbar im Raumverzeichnis",
|
||||
creator: "Ersteller",
|
||||
join_rules: "Beitrittsregeln",
|
||||
guest_access: "Gastzugriff",
|
||||
history_visibility: "Historie-Sichtbarkeit",
|
||||
topic: "Thema",
|
||||
avatar: "Avatar",
|
||||
},
|
||||
enums: {
|
||||
join_rules: {
|
||||
@ -205,6 +203,13 @@ export default {
|
||||
},
|
||||
unencrypted: "Nicht verschlüsselt",
|
||||
},
|
||||
action: {
|
||||
erase: {
|
||||
title: "Raum löschen",
|
||||
content:
|
||||
"Sind Sie sicher dass Sie den Raum löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden. Alle Nachrichten und Medien, die der Raum beinhaltet werden vom Server gelöscht!",
|
||||
},
|
||||
},
|
||||
},
|
||||
reports: {
|
||||
name: "Ereignisbericht |||| Ereignisberichte",
|
||||
@ -268,6 +273,23 @@ export default {
|
||||
last_access_ts: "Letzter Zugriff",
|
||||
},
|
||||
},
|
||||
delete_media: {
|
||||
name: "Medien",
|
||||
fields: {
|
||||
before_ts: "Letzter Zugriff vor",
|
||||
size_gt: "Größer als (in Bytes)",
|
||||
keep_profiles: "Behalte Profilbilder",
|
||||
},
|
||||
action: {
|
||||
send: "Medien löschen",
|
||||
send_success: "Anfrage erfolgreich versendet.",
|
||||
send_failure: "Beim Versenden ist ein Fehler aufgetreten.",
|
||||
},
|
||||
helper: {
|
||||
send:
|
||||
"Diese API löscht die lokalen Medien von der Festplatte des eigenen Servers. Dies umfasst alle lokalen Miniaturbilder und Kopien von Medien. Diese API wirkt sich nicht auf Medien aus, die sich in externen Medien-Repositories befinden.",
|
||||
},
|
||||
},
|
||||
pushers: {
|
||||
name: "Pusher |||| Pushers",
|
||||
fields: {
|
||||
@ -305,6 +327,32 @@ export default {
|
||||
media_length: "Größe der Dateien",
|
||||
},
|
||||
},
|
||||
room_state: {
|
||||
name: "Zustandsereignisse",
|
||||
fields: {
|
||||
type: "Typ",
|
||||
content: "Inhalt",
|
||||
origin_server_ts: "Sendezeit",
|
||||
sender: "Absender",
|
||||
},
|
||||
},
|
||||
room_directory: {
|
||||
name: "Raumverzeichnis",
|
||||
fields: {
|
||||
world_readable: "Gastbenutzer dürfen ohne Beitritt lesen",
|
||||
guest_can_join: "Gastbenutzer dürfen beitreten",
|
||||
},
|
||||
action: {
|
||||
title:
|
||||
"Raum aus Verzeichnis löschen |||| %{smart_count} Räume aus Verzeichnis löschen",
|
||||
content:
|
||||
"Möchten Sie den Raum wirklich aus dem Raumverzeichnis löschen? |||| Möchten Sie die %{smart_count} Räume wirklich aus dem Raumverzeichnis löschen?",
|
||||
erase: "Lösche aus Verzeichnis",
|
||||
create: "Eintragen ins Verzeichnis",
|
||||
send_success: "Raum erfolgreich eingetragen.",
|
||||
send_failure: "Beim Entfernen ist ein Fehler aufgetreten.",
|
||||
},
|
||||
},
|
||||
},
|
||||
ra: {
|
||||
...germanMessages.ra,
|
||||
|
@ -36,11 +36,6 @@ export default {
|
||||
detail: "Details",
|
||||
permission: "Permissions",
|
||||
},
|
||||
delete: {
|
||||
title: "Delete room",
|
||||
message:
|
||||
"Are you sure you want to delete the room? This cannot be undone. All messages and shared media in the room will be deleted from the server!",
|
||||
},
|
||||
},
|
||||
reports: { tabs: { basic: "Basic", detail: "Details" } },
|
||||
},
|
||||
@ -173,16 +168,19 @@ export default {
|
||||
invite_members: "Invite Members",
|
||||
invitees: "Invitations",
|
||||
joined_local_members: "Local members",
|
||||
state_events: "State events",
|
||||
joined_local_devices: "Local devices",
|
||||
state_events: "State events / Complexity",
|
||||
version: "Version",
|
||||
is_encrypted: "Encrypted",
|
||||
encryption: "Encryption",
|
||||
federatable: "Federatable",
|
||||
public: "Public",
|
||||
public: "Visible in room directory",
|
||||
creator: "Creator",
|
||||
join_rules: "Join rules",
|
||||
guest_access: "Guest access",
|
||||
history_visibility: "History visibility",
|
||||
topic: "Topic",
|
||||
avatar: "Avatar",
|
||||
},
|
||||
enums: {
|
||||
join_rules: {
|
||||
@ -203,6 +201,11 @@ export default {
|
||||
},
|
||||
unencrypted: "Unencrypted",
|
||||
},
|
||||
erase: {
|
||||
title: "Delete room",
|
||||
content:
|
||||
"Are you sure you want to delete the room? This cannot be undone. All messages and shared media in the room will be deleted from the server!",
|
||||
},
|
||||
},
|
||||
reports: {
|
||||
name: "Reported event |||| Reported events",
|
||||
@ -266,6 +269,23 @@ export default {
|
||||
last_access_ts: "Last access",
|
||||
},
|
||||
},
|
||||
delete_media: {
|
||||
name: "Media",
|
||||
fields: {
|
||||
before_ts: "last access before",
|
||||
size_gt: "Larger then (in bytes)",
|
||||
keep_profiles: "Keep profile images",
|
||||
},
|
||||
action: {
|
||||
send: "Delete media",
|
||||
send_success: "Request successfully sent.",
|
||||
send_failure: "An error has occurred.",
|
||||
},
|
||||
helper: {
|
||||
send:
|
||||
"This API deletes the local media from the disk of your own server. This includes any local thumbnails and copies of media downloaded. This API will not affect media that has been uploaded to external media repositories.",
|
||||
},
|
||||
},
|
||||
pushers: {
|
||||
name: "Pusher |||| Pushers",
|
||||
fields: {
|
||||
@ -303,5 +323,31 @@ export default {
|
||||
media_length: "Media length",
|
||||
},
|
||||
},
|
||||
room_state: {
|
||||
name: "State events",
|
||||
fields: {
|
||||
type: "Type",
|
||||
content: "Content",
|
||||
origin_server_ts: "time of send",
|
||||
sender: "Sender",
|
||||
},
|
||||
},
|
||||
room_directory: {
|
||||
name: "Room directory",
|
||||
fields: {
|
||||
world_readable: "guest users may view without joining",
|
||||
guest_can_join: "guest users may join",
|
||||
},
|
||||
action: {
|
||||
title:
|
||||
"Delete room from directory |||| Delete %{smart_count} rooms from directory",
|
||||
content:
|
||||
"Are you sure you want to remove this room from directory? |||| Are you sure you want to remove these %{smart_count} rooms from directory",
|
||||
erase: "Delete from room directory",
|
||||
create: "Publish in room directory",
|
||||
send_success: "Room successfully published.",
|
||||
send_failure: "An error has occurred.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
290
src/i18n/zh.js
Normal file
290
src/i18n/zh.js
Normal file
@ -0,0 +1,290 @@
|
||||
import chineseMessages from "ra-language-chinese";
|
||||
|
||||
export default {
|
||||
...chineseMessages,
|
||||
synapseadmin: {
|
||||
auth: {
|
||||
base_url: "服务器 URL",
|
||||
welcome: "欢迎来到 Synapse-admin",
|
||||
server_version: "Synapse 版本",
|
||||
username_error: "请输入完整有效的用户 ID: '@user:domain'",
|
||||
protocol_error: "URL 需要以'http://'或'https://'作为起始",
|
||||
url_error: "不是一个有效的 Matrix 服务器地址",
|
||||
},
|
||||
users: {
|
||||
invalid_user_id:
|
||||
"必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver",
|
||||
},
|
||||
rooms: {
|
||||
tabs: {
|
||||
basic: "基本",
|
||||
members: "成员",
|
||||
detail: "细节",
|
||||
permission: "权限",
|
||||
},
|
||||
delete: {
|
||||
title: "删除房间",
|
||||
message:
|
||||
"您确定要删除这个房间吗?该操作无法被撤销。这个房间里所有的消息和分享的媒体都将被从服务器上删除!",
|
||||
},
|
||||
},
|
||||
reports: { tabs: { basic: "基本", detail: "细节" } },
|
||||
},
|
||||
import_users: {
|
||||
error: {
|
||||
at_entry: "在条目 %{entry}: %{message}",
|
||||
error: "错误",
|
||||
required_field: "需要的值 '%{field}' 未被设置。",
|
||||
invalid_value:
|
||||
"第 %{row} 行出现无效值。 '%{field}' 只可以是 'true' 或 'false'。",
|
||||
unreasonably_big: "拒绝加载过大的文件: %{size} MB",
|
||||
already_in_progress: "一个导入进程已经在运行中",
|
||||
id_exits: "ID %{id} 已经存在",
|
||||
},
|
||||
title: "通过 CSV 导入用户",
|
||||
goToPdf: "转到 PDF",
|
||||
cards: {
|
||||
importstats: {
|
||||
header: "导入用户",
|
||||
users_total:
|
||||
"%{smart_count} 用户在 CSV 文件中 |||| %{smart_count} 用户在 CSV 文件中",
|
||||
guest_count: "%{smart_count} 访客 |||| %{smart_count} 访客",
|
||||
admin_count: "%{smart_count} 管理员 |||| %{smart_count} 管理员",
|
||||
},
|
||||
conflicts: {
|
||||
header: "冲突处理策略",
|
||||
mode: {
|
||||
stop: "在冲突处停止",
|
||||
skip: "显示错误并跳过冲突",
|
||||
},
|
||||
},
|
||||
ids: {
|
||||
header: "IDs",
|
||||
all_ids_present: "每条记录的 ID",
|
||||
count_ids_present:
|
||||
"%{smart_count} 个含 ID 的记录 |||| %{smart_count} 个含 ID 的记录",
|
||||
mode: {
|
||||
ignore: "忽略 CSV 中的 ID 并创建新的",
|
||||
update: "更新已经存在的记录",
|
||||
},
|
||||
},
|
||||
passwords: {
|
||||
header: "密码",
|
||||
all_passwords_present: "每条记录的密码",
|
||||
count_passwords_present:
|
||||
"%{smart_count} 个含密码的记录 |||| %{smart_count} 个含密码的记录",
|
||||
use_passwords: "使用 CSV 中标记的密码",
|
||||
},
|
||||
upload: {
|
||||
header: "导入 CSV 文件",
|
||||
explanation:
|
||||
"在这里,你可以上传一个用逗号分隔的文件,用于创建或更新用户。该文件必须包括 'id' 和 'displayname' 字段。你可以在这里下载并修改一个示例文件:",
|
||||
},
|
||||
startImport: {
|
||||
simulate_only: "模拟模式",
|
||||
run_import: "导入",
|
||||
},
|
||||
results: {
|
||||
header: "导入结果",
|
||||
total: "共计 %{smart_count} 条记录 |||| 共计 %{smart_count} 条记录",
|
||||
successful: "%{smart_count} 条记录导入成功",
|
||||
skipped: "跳过 %{smart_count} 条记录",
|
||||
download_skipped: "下载跳过的记录",
|
||||
with_error:
|
||||
"%{smart_count} 条记录出现错误 ||| %{smart_count} 条记录出现错误",
|
||||
simulated_only: "只是一次模拟运行",
|
||||
},
|
||||
},
|
||||
},
|
||||
resources: {
|
||||
users: {
|
||||
backtolist: "回到列表",
|
||||
name: "用户",
|
||||
email: "邮箱",
|
||||
msisdn: "电话",
|
||||
threepid: "邮箱 / 电话",
|
||||
fields: {
|
||||
avatar: "邮箱",
|
||||
id: "用户 ID",
|
||||
name: "用户名",
|
||||
is_guest: "访客",
|
||||
admin: "服务器管理员",
|
||||
deactivated: "被禁用",
|
||||
guests: "显示访客",
|
||||
show_deactivated: "显示被禁用的账户",
|
||||
user_id: "搜索用户",
|
||||
displayname: "显示名字",
|
||||
password: "密码",
|
||||
avatar_url: "头像 URL",
|
||||
avatar_src: "头像",
|
||||
medium: "Medium",
|
||||
threepids: "3PIDs",
|
||||
address: "地址",
|
||||
creation_ts_ms: "创建时间戳",
|
||||
consent_version: "协议版本",
|
||||
},
|
||||
helper: {
|
||||
deactivate: "您必须提供一串密码来激活账户。",
|
||||
erase: "将用户标记为根据 GDPR 的要求抹除了",
|
||||
},
|
||||
action: {
|
||||
erase: "抹除用户信息",
|
||||
},
|
||||
},
|
||||
rooms: {
|
||||
name: "房间",
|
||||
fields: {
|
||||
room_id: "房间 ID",
|
||||
name: "房间名",
|
||||
canonical_alias: "别名",
|
||||
joined_members: "成员",
|
||||
joined_local_members: "本地成员",
|
||||
state_events: "状态事件",
|
||||
version: "版本",
|
||||
is_encrypted: "已经加密",
|
||||
encryption: "加密",
|
||||
federatable: "可联合的",
|
||||
public: "公开",
|
||||
creator: "创建者",
|
||||
join_rules: "加入规则",
|
||||
guest_access: "访客访问",
|
||||
history_visibility: "历史可见性",
|
||||
},
|
||||
enums: {
|
||||
join_rules: {
|
||||
public: "公开",
|
||||
knock: "申请",
|
||||
invite: "邀请",
|
||||
private: "私有",
|
||||
},
|
||||
guest_access: {
|
||||
can_join: "访客可以加入",
|
||||
forbidden: "访客不可加入",
|
||||
},
|
||||
history_visibility: {
|
||||
invited: "自从被邀请",
|
||||
joined: "自从加入",
|
||||
shared: "自从分享",
|
||||
world_readable: "任何人",
|
||||
},
|
||||
unencrypted: "未加密",
|
||||
},
|
||||
},
|
||||
reports: {
|
||||
name: "报告事件",
|
||||
fields: {
|
||||
id: "ID",
|
||||
received_ts: "报告时间",
|
||||
user_id: "报告者",
|
||||
name: "房间名",
|
||||
score: "分数",
|
||||
reason: "原因",
|
||||
event_id: "事件 ID",
|
||||
event_json: {
|
||||
origin: "原始服务器",
|
||||
origin_server_ts: "发送时间",
|
||||
type: "事件类型",
|
||||
content: {
|
||||
msgtype: "内容类型",
|
||||
body: "内容",
|
||||
format: "格式",
|
||||
formatted_body: "格式化的数据",
|
||||
algorithm: "算法",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
connections: {
|
||||
name: "连接",
|
||||
fields: {
|
||||
last_seen: "日期",
|
||||
ip: "IP 地址",
|
||||
user_agent: "用户代理 (UA)",
|
||||
},
|
||||
},
|
||||
devices: {
|
||||
name: "设备",
|
||||
fields: {
|
||||
device_id: "设备 ID",
|
||||
display_name: "设备名",
|
||||
last_seen_ts: "时间戳",
|
||||
last_seen_ip: "IP 地址",
|
||||
},
|
||||
action: {
|
||||
erase: {
|
||||
title: "移除 %{id}",
|
||||
content: '您确定要移除设备 "%{name}"?',
|
||||
success: "设备移除成功。",
|
||||
failure: "出现了一个错误。",
|
||||
},
|
||||
},
|
||||
},
|
||||
users_media: {
|
||||
name: "媒体文件",
|
||||
fields: {
|
||||
media_id: "媒体文件 ID",
|
||||
media_length: "长度",
|
||||
media_type: "类型",
|
||||
upload_name: "文件名",
|
||||
quarantined_by: "被隔离",
|
||||
safe_from_quarantine: "取消隔离",
|
||||
created_ts: "创建",
|
||||
last_access_ts: "上一次访问",
|
||||
},
|
||||
},
|
||||
delete_media: {
|
||||
name: "媒体文件",
|
||||
fields: {
|
||||
before_ts: "最后访问时间",
|
||||
size_gt: "大于 (字节)",
|
||||
keep_profiles: "保留头像",
|
||||
},
|
||||
action: {
|
||||
send: "删除媒体",
|
||||
send_success: "请求发送成功。",
|
||||
send_failure: "出现了一个错误。",
|
||||
},
|
||||
helper: {
|
||||
send:
|
||||
"这个API会删除您硬盘上的本地媒体。包含了任何的本地缓存和下载的媒体备份。这个API不会影响上传到外部媒体存储库上的媒体文件。",
|
||||
},
|
||||
},
|
||||
pushers: {
|
||||
name: "发布者",
|
||||
fields: {
|
||||
app: "App",
|
||||
app_display_name: "App 名称",
|
||||
app_id: "App ID",
|
||||
device_display_name: "设备显示名",
|
||||
kind: "类型",
|
||||
lang: "语言",
|
||||
profile_tag: "数据标签",
|
||||
pushkey: "Pushkey",
|
||||
data: { url: "URL" },
|
||||
},
|
||||
},
|
||||
servernotices: {
|
||||
name: "服务器提示",
|
||||
send: "发送服务器提示",
|
||||
fields: {
|
||||
body: "信息",
|
||||
},
|
||||
action: {
|
||||
send: "发送提示",
|
||||
send_success: "服务器提示发送成功。",
|
||||
send_failure: "出现了一个错误。",
|
||||
},
|
||||
helper: {
|
||||
send:
|
||||
'向选中的用户发送服务器提示。服务器配置中的 "服务器提示(Server Notices)" 选项需要被设置为启用。',
|
||||
},
|
||||
},
|
||||
user_media_statistics: {
|
||||
name: "用户的媒体文件",
|
||||
fields: {
|
||||
media_count: "媒体文件统计",
|
||||
media_length: "媒体文件长度",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@ -160,6 +160,19 @@ const resourceMap = {
|
||||
return json.total;
|
||||
},
|
||||
},
|
||||
room_state: {
|
||||
map: rs => ({
|
||||
...rs,
|
||||
id: rs.event_id,
|
||||
}),
|
||||
reference: id => ({
|
||||
endpoint: `/_synapse/admin/v1/rooms/${id}/state`,
|
||||
}),
|
||||
data: "state",
|
||||
total: json => {
|
||||
return json.state.length;
|
||||
},
|
||||
},
|
||||
pushers: {
|
||||
map: p => ({
|
||||
...p,
|
||||
@ -203,6 +216,16 @@ const resourceMap = {
|
||||
)}/${params.id}`,
|
||||
}),
|
||||
},
|
||||
delete_media: {
|
||||
delete: params => ({
|
||||
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
|
||||
"home_server"
|
||||
)}/delete?before_ts=${params.before_ts}&size_gt=${
|
||||
params.size_gt
|
||||
}&keep_profiles=${params.keep_profiles}`,
|
||||
method: "POST",
|
||||
}),
|
||||
},
|
||||
servernotices: {
|
||||
map: n => ({ id: n.event_id }),
|
||||
create: data => ({
|
||||
@ -228,6 +251,30 @@ const resourceMap = {
|
||||
return json.total;
|
||||
},
|
||||
},
|
||||
room_directory: {
|
||||
path: "/_matrix/client/r0/publicRooms",
|
||||
map: rd => ({
|
||||
...rd,
|
||||
id: rd.room_id,
|
||||
public: !!rd.public,
|
||||
guest_access: !!rd.guest_access,
|
||||
avatar_src: mxcUrlToHttp(rd.avatar_url),
|
||||
}),
|
||||
data: "chunk",
|
||||
total: json => {
|
||||
return json.total_room_count_estimate;
|
||||
},
|
||||
create: params => ({
|
||||
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
|
||||
body: { visibility: "public" },
|
||||
method: "PUT",
|
||||
}),
|
||||
delete: params => ({
|
||||
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
|
||||
body: { visibility: "private" },
|
||||
method: "PUT",
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
function filterNullValues(key, value) {
|
||||
|
Loading…
Reference in New Issue
Block a user