diff --git a/README.md b/README.md index a0a8af1..67a8ff8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This project is built using [react-admin](https://marmelab.com/react-admin/). -It needs at least Synapse v1.32.0 for all functions to work as expected! +It needs at least Synapse v1.36.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). diff --git a/src/components/media.js b/src/components/media.js index 8d3c583..0595f8f 100644 --- a/src/components/media.js +++ b/src/components/media.js @@ -10,16 +10,20 @@ import { SaveButton, SimpleForm, Toolbar, + useCreate, useDelete, useNotify, + useRefresh, useTranslate, } from "react-admin"; -import IconCancel from "@material-ui/icons/Cancel"; +import DeleteSweepIcon from "@material-ui/icons/DeleteSweep"; 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"; +import IconCancel from "@material-ui/icons/Cancel"; +import LockIcon from "@material-ui/icons/Lock"; +import LockOpenIcon from "@material-ui/icons/LockOpen"; const useStyles = makeStyles( theme => ({ @@ -143,3 +147,64 @@ export const DeleteMediaButton = props => { ); }; + +export const ProtectMediaButton = props => { + const { record } = props; + const refresh = useRefresh(); + const notify = useNotify(); + const [create, { loading }] = useCreate("protect_media"); + const [deleteOne] = useDelete("protect_media"); + + if (!record) return null; + + const handleProtect = () => { + create( + { payload: { data: record } }, + { + onSuccess: () => { + notify("resources.protect_media.action.send_success"); + refresh(); + }, + onFailure: () => + notify("resources.protect_media.action.send_failure", "error"), + } + ); + }; + + const handleUnprotect = () => { + deleteOne( + { payload: { ...record } }, + { + onSuccess: () => { + notify("resources.protect_media.action.send_success"); + refresh(); + }, + onFailure: () => + notify("resources.protect_media.action.send_failure", "error"), + } + ); + }; + + return ( + + {!record.safe_from_quarantine && !record.quarantined_by && ( + + )} + {record.safe_from_quarantine && ( + + )} + + ); +}; diff --git a/src/components/users.js b/src/components/users.js index ecce484..6dfb380 100644 --- a/src/components/users.js +++ b/src/components/users.js @@ -1,12 +1,12 @@ import React, { cloneElement, Fragment } from "react"; import Avatar from "@material-ui/core/Avatar"; -import PersonPinIcon from "@material-ui/icons/PersonPin"; import ContactMailIcon from "@material-ui/icons/ContactMail"; import DevicesIcon from "@material-ui/icons/Devices"; import GetAppIcon from "@material-ui/icons/GetApp"; -import SettingsInputComponentIcon from "@material-ui/icons/SettingsInputComponent"; import NotificationsIcon from "@material-ui/icons/Notifications"; import PermMediaIcon from "@material-ui/icons/PermMedia"; +import PersonPinIcon from "@material-ui/icons/PersonPin"; +import SettingsInputComponentIcon from "@material-ui/icons/SettingsInputComponent"; import ViewListIcon from "@material-ui/icons/ViewList"; import { ArrayInput, @@ -47,6 +47,7 @@ import { } from "react-admin"; import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices"; import { DeviceRemoveButton } from "./devices"; +import { ProtectMediaButton } from "./media"; import { makeStyles } from "@material-ui/core/styles"; const redirect = (basePath, id, data) => { @@ -454,6 +455,7 @@ export const UserEdit = props => { + diff --git a/src/i18n/de.js b/src/i18n/de.js index e7de884..bb7c4d3 100644 --- a/src/i18n/de.js +++ b/src/i18n/de.js @@ -262,6 +262,14 @@ const de = { "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.", }, }, + protect_media: { + action: { + create: "Schützen", + delete: "Schutz aufheben", + send_success: "Erfolgreich den Schutz-Status geändert.", + send_failure: "Beim Versenden ist ein Fehler aufgetreten.", + }, + }, pushers: { name: "Pusher |||| Pushers", fields: { diff --git a/src/i18n/en.js b/src/i18n/en.js index 6cc60bc..93bfca3 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -258,6 +258,14 @@ const en = { "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.", }, }, + protect_media: { + action: { + create: "Protect", + delete: "Unprotect", + send_success: "Successfully changed the protection status.", + send_failure: "An error has occurred.", + }, + }, pushers: { name: "Pusher |||| Pushers", fields: { diff --git a/src/synapse/dataProvider.js b/src/synapse/dataProvider.js index bbe06cc..a3ac095 100644 --- a/src/synapse/dataProvider.js +++ b/src/synapse/dataProvider.js @@ -183,6 +183,17 @@ const resourceMap = { method: "POST", }), }, + protect_media: { + map: pm => ({ id: pm.media_id }), + create: params => ({ + endpoint: `/_synapse/admin/v1/media/protect/${params.media_id}`, + method: "POST", + }), + delete: params => ({ + endpoint: `/_synapse/admin/v1/media/unprotect/${params.media_id}`, + method: "POST", + }), + }, servernotices: { map: n => ({ id: n.event_id }), create: data => ({