Show links to media content from local users and reports (#508)

Change-Id: Ia8993470a9dd5e479c028013b5be6723f569814d
This commit is contained in:
Alexander Tumin 2024-04-20 10:29:44 +03:00 committed by Manuel Stahl
parent 531d8f2d7f
commit c6f9dbec18
6 changed files with 78 additions and 2 deletions

View file

@ -15,6 +15,7 @@ import {
useRecordContext, useRecordContext,
useTranslate, useTranslate,
} from "react-admin"; } from "react-admin";
import { MXCField } from "./media";
import PageviewIcon from "@mui/icons-material/Pageview"; import PageviewIcon from "@mui/icons-material/Pageview";
import ReportIcon from "@mui/icons-material/Warning"; import ReportIcon from "@mui/icons-material/Warning";
import ViewListIcon from "@mui/icons-material/ViewList"; import ViewListIcon from "@mui/icons-material/ViewList";
@ -89,6 +90,8 @@ export const ReportShow = props => {
<TextField source="event_json.type" /> <TextField source="event_json.type" />
<TextField source="event_json.content.msgtype" /> <TextField source="event_json.content.msgtype" />
<TextField source="event_json.content.body" /> <TextField source="event_json.content.body" />
<TextField source="event_json.content.info.mimetype" />
<MXCField source="event_json.content.url" />
<TextField source="event_json.content.format" /> <TextField source="event_json.content.format" />
<TextField source="event_json.content.formatted_body" /> <TextField source="event_json.content.formatted_body" />
<TextField source="event_json.content.algorithm" /> <TextField source="event_json.content.algorithm" />

View file

@ -1,4 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import get from "lodash/get";
import { import {
BooleanInput, BooleanInput,
Button, Button,
@ -14,10 +15,12 @@ import {
useRefresh, useRefresh,
useTranslate, useTranslate,
} from "react-admin"; } from "react-admin";
import { Link } from "react-router-dom";
import BlockIcon from "@mui/icons-material/Block"; import BlockIcon from "@mui/icons-material/Block";
import ClearIcon from "@mui/icons-material/Clear"; import ClearIcon from "@mui/icons-material/Clear";
import DeleteSweepIcon from "@mui/icons-material/DeleteSweep"; import DeleteSweepIcon from "@mui/icons-material/DeleteSweep";
import { import {
Box,
Dialog, Dialog,
DialogContent, DialogContent,
DialogContentText, DialogContentText,
@ -27,7 +30,9 @@ 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 FileOpenIcon from "@mui/icons-material/FileOpen";
import { alpha, useTheme } from "@mui/material/styles"; import { alpha, useTheme } from "@mui/material/styles";
import { getMediaUrl } from "../synapse/synapse";
const DeleteMediaDialog = ({ open, loading, onClose, onSubmit }) => { const DeleteMediaDialog = ({ open, loading, onClose, onSubmit }) => {
const translate = useTranslate(); const translate = useTranslate();
@ -333,3 +338,49 @@ export const QuarantineMediaButton = props => {
</> </>
); );
}; };
export const ViewMediaButton = ({ media_id, label }) => {
const translate = useTranslate();
const url = getMediaUrl(media_id);
return (
<Box style={{ whiteSpace: "pre" }}>
<Tooltip title={translate("resources.users_media.action.open")}>
<span>
<Button
component={Link}
to={url}
target="_blank"
rel="noopener"
style={{ minWidth: 0, paddingLeft: 0, paddingRight: 0 }}
>
<FileOpenIcon />
</Button>
</span>
</Tooltip>
{label}
</Box>
);
};
export const MediaIDField = ({ source }) => {
const homeserver = localStorage.getItem("home_server");
const record = useRecordContext();
if (!record) return null;
const src = get(record, source)?.toString();
if (!src) return null;
return <ViewMediaButton media_id={`${homeserver}/${src}`} label={src} />;
};
export const MXCField = ({ source }) => {
const record = useRecordContext();
if (!record) return null;
const src = get(record, source)?.toString();
if (!src) return null;
const media_id = src.replace("mxc://", "");
return <ViewMediaButton media_id={media_id} label={src} />;
};

View file

@ -51,7 +51,11 @@ import { Link } from "react-router-dom";
import AvatarField from "./AvatarField"; 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 {
MediaIDField,
ProtectMediaButton,
QuarantineMediaButton,
} from "./media";
const choices_medium = [ const choices_medium = [
{ id: "email", name: "resources.users.email" }, { id: "email", name: "resources.users.email" },
@ -449,13 +453,13 @@ export const UserEdit = props => {
sort={{ field: "created_ts", order: "DESC" }} sort={{ field: "created_ts", order: "DESC" }}
> >
<Datagrid style={{ width: "100%" }}> <Datagrid style={{ width: "100%" }}>
<MediaIDField source="media_id" />
<DateField source="created_ts" showTime options={date_format} /> <DateField source="created_ts" showTime options={date_format} />
<DateField <DateField
source="last_access_ts" source="last_access_ts"
showTime showTime
options={date_format} options={date_format}
/> />
<TextField source="media_id" />
<NumberField source="media_length" /> <NumberField source="media_length" />
<TextField source="media_type" /> <TextField source="media_type" />
<TextField source="upload_name" /> <TextField source="upload_name" />

View file

@ -208,6 +208,9 @@ const de = {
format: "Nachrichtenformat", format: "Nachrichtenformat",
formatted_body: "Formatierter Nachrichteninhalt", formatted_body: "Formatierter Nachrichteninhalt",
algorithm: "Verschlüsselungsalgorithmus", algorithm: "Verschlüsselungsalgorithmus",
info: {
mimetype: "Typ",
},
}, },
}, },
}, },
@ -256,6 +259,9 @@ const de = {
created_ts: "Erstellt", created_ts: "Erstellt",
last_access_ts: "Letzter Zugriff", last_access_ts: "Letzter Zugriff",
}, },
action: {
open: "Mediendatei in neuem Fenster öffnen",
},
}, },
delete_media: { delete_media: {
name: "Medien", name: "Medien",

View file

@ -205,6 +205,10 @@ const en = {
format: "format", format: "format",
formatted_body: "formatted content", formatted_body: "formatted content",
algorithm: "algorithm", algorithm: "algorithm",
url: "URL",
info: {
mimetype: "Type",
},
}, },
}, },
}, },
@ -253,6 +257,9 @@ const en = {
created_ts: "Created", created_ts: "Created",
last_access_ts: "Last access", last_access_ts: "Last access",
}, },
action: {
open: "Open media file in new window",
},
}, },
delete_media: { delete_media: {
name: "Media", name: "Media",

View file

@ -53,3 +53,8 @@ export const getSupportedLoginFlows = async baseUrl => {
const response = await fetchUtils.fetchJson(loginFlowsUrl, { method: "GET" }); const response = await fetchUtils.fetchJson(loginFlowsUrl, { method: "GET" });
return response.json.flows; return response.json.flows;
}; };
export const getMediaUrl = media_id => {
const baseUrl = localStorage.getItem("base_url");
return `${baseUrl}/_matrix/media/v1/download/${media_id}?allow_redirect=true`;
};