Add buttons for blocking and restoring all of a user's files
This commit is contained in:
@@ -8,7 +8,7 @@ import UserManagement from "./UserManagement.svelte";
|
||||
import EmailReporters from "./EmailReporters.svelte";
|
||||
import MollieSettlements from "./MollieSettlements.svelte";
|
||||
import PayPalTaxes from "./PayPalTaxes.svelte";
|
||||
import UserBans from "./UserBans.svelte";
|
||||
import UserBans from "./user_bans/UserBans.svelte";
|
||||
|
||||
let pages = [
|
||||
{
|
||||
|
64
svelte/src/admin_panel/user_bans/BanDetails.svelte
Normal file
64
svelte/src/admin_panel/user_bans/BanDetails.svelte
Normal file
@@ -0,0 +1,64 @@
|
||||
<script>
|
||||
import Euro from "../../util/Euro.svelte";
|
||||
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte";
|
||||
|
||||
export let row = {}
|
||||
</script>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td>{row.user.username}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>{row.user_id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Email</td>
|
||||
<td>{row.user.email}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Subscription</td>
|
||||
<td>{row.user.subscription.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Credit balance</td>
|
||||
<td><Euro amount={row.user.balance_micro_eur}/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Storage used</td>
|
||||
<td>{formatDataVolume(row.user.storage_space_used, 3)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FS Storage used</td>
|
||||
<td>{formatDataVolume(row.user.filesystem_storage_used, 3)}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
<div class="table_scroll">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Reason</td>
|
||||
<td>Reporter</td>
|
||||
<td>Ban time</td>
|
||||
<td>Expire time</td>
|
||||
<td>File</td>
|
||||
</tr>
|
||||
{#each row.offences as offence (offence.ban_time)}
|
||||
<tr>
|
||||
<td>{offence.reason}</td>
|
||||
<td>{offence.reporter}</td>
|
||||
<td>{formatDate(offence.ban_time, true, true, false)}</td>
|
||||
<td>{formatDate(offence.expire_time, true, true, false)}</td>
|
||||
<td>
|
||||
{#if offence.file_link}
|
||||
<a href={offence.file_link} target="_blank" rel="noreferrer">
|
||||
{offence.file_name}
|
||||
</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
</div>
|
@@ -1,10 +1,12 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { formatDataVolume, formatDate } from "../util/Formatting.svelte";
|
||||
import Expandable from "../util/Expandable.svelte";
|
||||
import LoadingIndicator from "../util/LoadingIndicator.svelte";
|
||||
import Button from "../layout/Button.svelte"
|
||||
import Euro from "../util/Euro.svelte"
|
||||
import { formatDate } from "../../util/Formatting.svelte";
|
||||
import Expandable from "../../util/Expandable.svelte";
|
||||
import LoadingIndicator from "../../util/LoadingIndicator.svelte";
|
||||
import Button from "../../layout/Button.svelte"
|
||||
import UserFiles from "./UserFiles.svelte";
|
||||
import BanDetails from "./BanDetails.svelte";
|
||||
import UserLists from "./UserLists.svelte";
|
||||
|
||||
let loading = true
|
||||
let rows = []
|
||||
@@ -66,6 +68,40 @@ const impersonate = async user_id => {
|
||||
window.open("/user", "_blank")
|
||||
}
|
||||
|
||||
const block_all_files = async (row, reason) => {
|
||||
const form = new FormData()
|
||||
form.append("user_id", row.user_id)
|
||||
form.append("abuse_type", reason)
|
||||
|
||||
loading = true;
|
||||
try {
|
||||
const req = await fetch(
|
||||
window.api_endpoint+"/admin/block_user_files",
|
||||
{ method: "POST", body: form }
|
||||
);
|
||||
if(req.status >= 400) {
|
||||
alert(await req.text())
|
||||
return
|
||||
}
|
||||
|
||||
const resp = await req.json()
|
||||
|
||||
if (reason === "none") {
|
||||
alert("Restored "+resp.files_blocked.length+" files")
|
||||
} else {
|
||||
alert("Blocked "+resp.files_blocked.length+" files")
|
||||
}
|
||||
|
||||
if (row.tab_element && row.tab_element.reload) {
|
||||
row.tab_element.reload()
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(get_bans);
|
||||
</script>
|
||||
|
||||
@@ -112,64 +148,63 @@ onMount(get_bans);
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Button click={() => impersonate(row.user_id)} icon="login" label="Impersonate user"/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td>{row.user.username}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>{row.user_id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Email</td>
|
||||
<td>{row.user.email}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Subscription</td>
|
||||
<td>{row.user.subscription.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Credit balance</td>
|
||||
<td><Euro amount={row.user.balance_micro_eur}/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Storage used</td>
|
||||
<td>{formatDataVolume(row.user.storage_space_used, 3)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FS Storage used</td>
|
||||
<td>{formatDataVolume(row.user.filesystem_storage_used, 3)}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
<div class="table_scroll">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Reason</td>
|
||||
<td>Reporter</td>
|
||||
<td>Ban time</td>
|
||||
<td>Expire time</td>
|
||||
<td>File</td>
|
||||
</tr>
|
||||
{#each row.offences as offence (offence.ban_time)}
|
||||
<tr>
|
||||
<td>{offence.reason}</td>
|
||||
<td>{offence.reporter}</td>
|
||||
<td>{formatDate(offence.ban_time, true, true, false)}</td>
|
||||
<td>{formatDate(offence.expire_time, true, true, false)}</td>
|
||||
<td>
|
||||
{#if offence.file_link}
|
||||
<a href={offence.file_link} target="_blank" rel="noreferrer">
|
||||
{offence.file_name}
|
||||
</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
<div class="toolbar">
|
||||
<Button click={() => impersonate(row.user_id)} icon="login" label="Impersonate user"/>
|
||||
<div class="toolbar_spacer"></div>
|
||||
<div class="toolbar_label">
|
||||
<i class="icon">block</i> Block all files
|
||||
<select bind:value={row.select_abuse_type}>
|
||||
<option>copyright</option>
|
||||
<option>child_abuse</option>
|
||||
<option>zoophilia</option>
|
||||
<option>terrorism</option>
|
||||
<option>gore</option>
|
||||
<option>malware</option>
|
||||
<option>doxing</option>
|
||||
<option>revenge_porn</option>
|
||||
</select>
|
||||
<Button
|
||||
click={() => block_all_files(row, row.select_abuse_type)}
|
||||
label="Go"
|
||||
/>
|
||||
</div>
|
||||
<div class="toolbar_spacer"></div>
|
||||
<Button
|
||||
click={() => block_all_files(row, "none")}
|
||||
icon="undo"
|
||||
label="Restore all files"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="tab_bar">
|
||||
<Button
|
||||
icon="person"
|
||||
label="User details"
|
||||
highlight={row.tab === undefined || row.tab === "user"}
|
||||
click={() => row.tab = "user"}
|
||||
/>
|
||||
<Button
|
||||
icon="image"
|
||||
label="Files"
|
||||
highlight={row.tab === "files"}
|
||||
click={() => row.tab = "files"}
|
||||
/>
|
||||
<Button
|
||||
icon="photo_library"
|
||||
label="Lists"
|
||||
highlight={row.tab === "lists"}
|
||||
click={() => row.tab = "lists"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if row.tab === undefined || row.tab === "user"}
|
||||
<BanDetails row={row} />
|
||||
{:else if row.tab === "files"}
|
||||
<UserFiles bind:this={row.tab_element} user_id={row.user_id} />
|
||||
{:else if row.tab === "lists"}
|
||||
<UserLists bind:this={row.tab_element} user_id={row.user_id} />
|
||||
{/if}
|
||||
|
||||
</Expandable>
|
||||
{/each}
|
||||
</section>
|
||||
@@ -177,6 +212,7 @@ onMount(get_bans);
|
||||
<style>
|
||||
.toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
@@ -184,7 +220,10 @@ onMount(get_bans);
|
||||
}
|
||||
.toolbar > * { flex: 0 0 auto; }
|
||||
.toolbar_spacer { flex: 1 1 auto; }
|
||||
.toolbar_label { margin: 5px; }
|
||||
.toolbar_label {
|
||||
display: block;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
|
||||
.header {
|
||||
@@ -204,4 +243,7 @@ onMount(get_bans);
|
||||
border-left: 1px solid var(--separator);
|
||||
text-align: center;
|
||||
}
|
||||
.tab_bar {
|
||||
border-bottom: 2px solid var(--separator);
|
||||
}
|
||||
</style>
|
120
svelte/src/admin_panel/user_bans/UserFiles.svelte
Normal file
120
svelte/src/admin_panel/user_bans/UserFiles.svelte
Normal file
@@ -0,0 +1,120 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import LoadingIndicator from "../../util/LoadingIndicator.svelte";
|
||||
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte";
|
||||
import SortButton from "../SortButton.svelte";
|
||||
|
||||
export let user_id = ""
|
||||
let files = []
|
||||
let loading = true
|
||||
|
||||
onMount(() => reload())
|
||||
|
||||
export const reload = async () => {
|
||||
try {
|
||||
const req = await fetch(
|
||||
window.api_endpoint+"/user/files",
|
||||
{
|
||||
headers: {
|
||||
"Admin-User-Override": user_id,
|
||||
}
|
||||
}
|
||||
);
|
||||
if(req.status >= 400) {
|
||||
alert(await req.text())
|
||||
return
|
||||
}
|
||||
|
||||
files = (await req.json()).files
|
||||
sort("")
|
||||
} catch (err) {
|
||||
alert(err);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
let sort_field = "date_upload"
|
||||
let asc = false
|
||||
const sort = (field) => {
|
||||
if (field !== "" && field === sort_field) {
|
||||
asc = !asc
|
||||
}
|
||||
if (field === "") {
|
||||
field = sort_field
|
||||
}
|
||||
sort_field = field
|
||||
|
||||
console.log("sorting by", field, "asc", asc)
|
||||
files.sort((a, b) => {
|
||||
if (typeof (a[field]) === "number") {
|
||||
// Sort ints from high to low
|
||||
if (asc) {
|
||||
return a[field] - b[field]
|
||||
} else {
|
||||
return b[field] - a[field]
|
||||
}
|
||||
} else {
|
||||
// Sort strings alphabetically
|
||||
if (asc) {
|
||||
return a[field].localeCompare(b[field])
|
||||
} else {
|
||||
return b[field].localeCompare(a[field])
|
||||
}
|
||||
}
|
||||
})
|
||||
files = files
|
||||
}
|
||||
</script>
|
||||
|
||||
<LoadingIndicator loading={loading}/>
|
||||
|
||||
<div class="table_scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><SortButton field="name" active_field={sort_field} asc={asc} sort_func={sort}>Name</SortButton></td>
|
||||
<td><SortButton field="abuse_type" active_field={sort_field} asc={asc} sort_func={sort}>Abuse</SortButton></td>
|
||||
<td><SortButton field="size" active_field={sort_field} asc={asc} sort_func={sort}>Size</SortButton></td>
|
||||
<td><SortButton field="views" active_field={sort_field} asc={asc} sort_func={sort}>V</SortButton></td>
|
||||
<td><SortButton field="downloads" active_field={sort_field} asc={asc} sort_func={sort}>DL</SortButton></td>
|
||||
<td><SortButton field="date_upload" active_field={sort_field} asc={asc} sort_func={sort}>Created</SortButton></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each files as file (file.id)}
|
||||
<tr>
|
||||
<td style="padding: 0;">
|
||||
<img src="{window.api_endpoint+file.thumbnail_href}?height=32&width=32" alt="thumbnail" class="thumbnail" />
|
||||
</td>
|
||||
<td>
|
||||
<a href="/u/{file.id}" target="_blank">{file.name}</a>
|
||||
</td>
|
||||
<td>
|
||||
{file.abuse_type}
|
||||
</td>
|
||||
<td>
|
||||
{formatDataVolume(file.size, 3)}
|
||||
</td>
|
||||
<td>
|
||||
{file.views}
|
||||
</td>
|
||||
<td>
|
||||
{file.downloads}
|
||||
</td>
|
||||
<td>
|
||||
{formatDate(file.date_upload, true, true)}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.thumbnail {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
</style>
|
74
svelte/src/admin_panel/user_bans/UserLists.svelte
Normal file
74
svelte/src/admin_panel/user_bans/UserLists.svelte
Normal file
@@ -0,0 +1,74 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import LoadingIndicator from "../../util/LoadingIndicator.svelte";
|
||||
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte";
|
||||
|
||||
export let user_id = ""
|
||||
let lists = []
|
||||
let loading = true
|
||||
|
||||
onMount(() => reload())
|
||||
|
||||
export const reload = async () => {
|
||||
try {
|
||||
const req = await fetch(
|
||||
window.api_endpoint+"/user/lists",
|
||||
{
|
||||
headers: {
|
||||
"Admin-User-Override": user_id,
|
||||
}
|
||||
}
|
||||
);
|
||||
if(req.status >= 400) {
|
||||
alert(await req.text())
|
||||
return
|
||||
}
|
||||
|
||||
lists = (await req.json()).lists
|
||||
} catch (err) {
|
||||
alert(err);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<LoadingIndicator loading={loading}/>
|
||||
|
||||
<div class="table_scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Name</td>
|
||||
<td>Files</td>
|
||||
<td>Created</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each lists as list (list.id)}
|
||||
<tr>
|
||||
<td style="padding: 0;">
|
||||
<img src="{window.api_endpoint}/list/{list.id}/thumbnail?height=32&width=32" alt="thumbnail" class="thumbnail" />
|
||||
</td>
|
||||
<td>
|
||||
<a href="/l/{list.id}" target="_blank">{list.title}</a>
|
||||
</td>
|
||||
<td>
|
||||
{list.file_count}
|
||||
</td>
|
||||
<td>
|
||||
{formatDate(list.date_created, true, true, true)}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.thumbnail {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user