From e6f01f035ba68f4693003c9de6527073095fdaa5 Mon Sep 17 00:00:00 2001 From: Dirk Klimpel <5740567+dklimpel@users.noreply.github.com> Date: Thu, 19 Aug 2021 11:51:07 +0200 Subject: [PATCH] Add buttons to protect and unprotect users' media from quarantine (#150) --- src/components/media.js | 102 +++++++++++++++++++++++++++++++++++- src/components/users.js | 7 +-- src/i18n/de.js | 9 ++++ src/i18n/en.js | 9 ++++ src/synapse/dataProvider.js | 11 ++++ 5 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/components/media.js b/src/components/media.js index 8d3c583..095f1ad 100644 --- a/src/components/media.js +++ b/src/components/media.js @@ -2,6 +2,7 @@ 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 { Tooltip } from "@material-ui/core"; import { BooleanInput, Button, @@ -10,16 +11,21 @@ import { SaveButton, SimpleForm, Toolbar, + useCreate, useDelete, useNotify, + useRefresh, useTranslate, } from "react-admin"; -import IconCancel from "@material-ui/icons/Cancel"; +import ClearIcon from "@material-ui/icons/Clear"; +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 +149,95 @@ export const DeleteMediaButton = props => { ); }; + +export const ProtectMediaButton = props => { + const { record } = props; + const translate = useTranslate(); + 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 ( + /* + Wrapping Tooltip with
+ https://github.com/marmelab/react-admin/issues/4349#issuecomment-578594735 + */ + + {record.quarantined_by && ( + +
+ {/* + Button instead BooleanField for + consistent appearance and position in the column + */} + +
+
+ )} + {record.safe_from_quarantine && ( + +
+ +
+
+ )} + {!record.safe_from_quarantine && !record.quarantined_by && ( + +
+ +
+
+ )} +
+ ); +}; diff --git a/src/components/users.js b/src/components/users.js index 8e198c9..32cbdd6 100644 --- a/src/components/users.js +++ b/src/components/users.js @@ -1,13 +1,13 @@ import React, { cloneElement, Fragment } from "react"; import Avatar from "@material-ui/core/Avatar"; -import PersonPinIcon from "@material-ui/icons/PersonPin"; import AssignmentIndIcon from "@material-ui/icons/AssignmentInd"; 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, @@ -48,6 +48,7 @@ import { import { Link } from "react-router-dom"; import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices"; import { DeviceRemoveButton } from "./devices"; +import { ProtectMediaButton } from "./media"; import { makeStyles } from "@material-ui/core/styles"; const redirect = () => { @@ -465,7 +466,7 @@ export const UserEdit = props => { - + diff --git a/src/i18n/de.js b/src/i18n/de.js index 7de0286..7a9139f 100644 --- a/src/i18n/de.js +++ b/src/i18n/de.js @@ -263,6 +263,15 @@ const de = { 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.", }, }, + protect_media: { + action: { + create: "Ungeschützt, Schutz erstellen", + delete: "Geschützt, Schutz aufheben", + none: "In Quarantäne", + 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 b8f239e..680fcc5 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -259,6 +259,15 @@ const en = { 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.", }, }, + protect_media: { + action: { + create: "Unprotected, create protection", + delete: "Protected, remove protection", + none: "In quarantine", + 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 3bfa4e7..d40640a 100644 --- a/src/synapse/dataProvider.js +++ b/src/synapse/dataProvider.js @@ -182,6 +182,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 => ({