From 0148b08b08deb8c8090c8ba8cd3f76c4eefd5444 Mon Sep 17 00:00:00 2001
From: dklimpel <5740567+dklimpel@users.noreply.github.com>
Date: Tue, 23 Feb 2021 13:37:54 +0100
Subject: [PATCH] Add button to delete media by size and date
---
src/components/media.js | 145 +++++++++++++++++++++++++++++++++++
src/components/statistics.js | 43 ++++++++++-
src/i18n/de.js | 17 ++++
src/i18n/en.js | 17 ++++
src/synapse/dataProvider.js | 10 +++
5 files changed, 230 insertions(+), 2 deletions(-)
create mode 100644 src/components/media.js
diff --git a/src/components/media.js b/src/components/media.js
new file mode 100644
index 0000000..8d3c583
--- /dev/null
+++ b/src/components/media.js
@@ -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 (
+
+ }
+ />
+
+
+ );
+ };
+
+ return (
+
+ );
+};
+
+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 (
+
+
+
+
+ );
+};
diff --git a/src/components/statistics.js b/src/components/statistics.js
index f14a2fa..74e6b70 100644
--- a/src/components/statistics.js
+++ b/src/components/statistics.js
@@ -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 (
+
+ {filters &&
+ cloneElement(filters, {
+ resource,
+ showFilter,
+ displayedFilters,
+ filterValues,
+ context: "button",
+ })}
+
+
+
+ );
+};
const UserMediaStatsPagination = props => (
@@ -23,6 +61,7 @@ export const UserMediaStatsList = props => {
return (
}
filters={}
pagination={}
sort={{ field: "media_length", order: "DESC" }}
diff --git a/src/i18n/de.js b/src/i18n/de.js
index f20ef18..3f44c96 100644
--- a/src/i18n/de.js
+++ b/src/i18n/de.js
@@ -236,6 +236,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: {
diff --git a/src/i18n/en.js b/src/i18n/en.js
index bb3ac3b..c4abdae 100644
--- a/src/i18n/en.js
+++ b/src/i18n/en.js
@@ -233,6 +233,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: {
diff --git a/src/synapse/dataProvider.js b/src/synapse/dataProvider.js
index d928462..2dae5dc 100644
--- a/src/synapse/dataProvider.js
+++ b/src/synapse/dataProvider.js
@@ -160,6 +160,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 => ({