diff options
Diffstat (limited to 'web/source/settings/views/moderation/domain-permissions/subscriptions/detail.tsx')
-rw-r--r-- | web/source/settings/views/moderation/domain-permissions/subscriptions/detail.tsx | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/web/source/settings/views/moderation/domain-permissions/subscriptions/detail.tsx b/web/source/settings/views/moderation/domain-permissions/subscriptions/detail.tsx new file mode 100644 index 000000000..408d81b92 --- /dev/null +++ b/web/source/settings/views/moderation/domain-permissions/subscriptions/detail.tsx @@ -0,0 +1,384 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import React, { useState } from "react"; +import { useLocation, useParams } from "wouter"; +import { useBaseUrl } from "../../../../lib/navigation/util"; +import BackButton from "../../../../components/back-button"; +import { useGetDomainPermissionSubscriptionQuery, useRemoveDomainPermissionSubscriptionMutation, useUpdateDomainPermissionSubscriptionMutation } from "../../../../lib/query/admin/domain-permissions/subscriptions"; +import { useBoolInput, useNumberInput, useTextInput } from "../../../../lib/form"; +import FormWithData from "../../../../lib/form/form-with-data"; +import { DomainPermSub } from "../../../../lib/types/domain-permission"; +import MutationButton from "../../../../components/form/mutation-button"; +import { Checkbox, NumberInput, Select, TextInput } from "../../../../components/form/inputs"; +import useFormSubmit from "../../../../lib/form/submit"; +import UsernameLozenge from "../../../../components/username-lozenge"; +import { urlValidator } from "../../../../lib/util/formvalidators"; + +export default function DomainPermissionSubscriptionDetail() { + const params = useParams(); + let id = params.permSubId as string | undefined; + if (!id) { + throw "no permSub ID"; + } + + return ( + <FormWithData + dataQuery={useGetDomainPermissionSubscriptionQuery} + queryArg={id} + DataForm={DomainPermSubForm} + /> + ); +} + +function DomainPermSubForm({ data: permSub }: { data: DomainPermSub }) { + const baseUrl = useBaseUrl(); + const backLocation: string = history.state?.backLocation ?? `~${baseUrl}/subscriptions/search`; + + return ( + <div className="domain-permission-subscription-details"> + <h1><BackButton to={backLocation} /> Domain Permission Subscription Detail</h1> + <DomainPermSubDetails permSub={permSub} /> + <UpdateDomainPermSub permSub={permSub} /> + <DeleteDomainPermSub permSub={permSub} backLocation={backLocation} /> + </div> + ); +} + +function DomainPermSubDetails({ permSub }: { permSub: DomainPermSub }) { + const [ location ] = useLocation(); + const baseUrl = useBaseUrl(); + + const permType = permSub.permission_type; + if (!permType) { + throw "permission_type was undefined"; + } + + const created = new Date(permSub.created_at).toDateString(); + let fetchedAtStr = "never"; + if (permSub.fetched_at) { + fetchedAtStr = new Date(permSub.fetched_at).toDateString(); + } + + let successfullyFetchedAtStr = "never"; + if (permSub.successfully_fetched_at) { + successfullyFetchedAtStr = new Date(permSub.successfully_fetched_at).toDateString(); + } + + return ( + <dl className="info-list"> + <div className="info-list-entry"> + <dt>Permission type:</dt> + <dd className={`permission-type ${permType}`}> + <i + aria-hidden={true} + className={`fa fa-${permType === "allow" ? "check" : "close"}`} + ></i> + {permType} + </dd> + </div> + <div className="info-list-entry"> + <dt>ID</dt> + <dd className="monospace">{permSub.id}</dd> + </div> + <div className="info-list-entry"> + <dt>Created</dt> + <dd><time dateTime={permSub.created_at}>{created}</time></dd> + </div> + <div className="info-list-entry"> + <dt>Created By</dt> + <dd> + <UsernameLozenge + account={permSub.created_by} + linkTo={`~/settings/moderation/accounts/${permSub.created_by}`} + backLocation={`~${baseUrl}${location}`} + /> + </dd> + </div> + <div className="info-list-entry"> + <dt>Last fetch attempt:</dt> + <dd>{fetchedAtStr}</dd> + </div> + <div className="info-list-entry"> + <dt>Last successful fetch:</dt> + <dd>{successfullyFetchedAtStr}</dd> + </div> + <div className="info-list-entry"> + <dt>Discovered {permSub.permission_type}s:</dt> + <dd>{permSub.count}</dd> + </div> + </dl> + ); +} + +function UpdateDomainPermSub({ permSub }: { permSub: DomainPermSub }) { + const [ showPassword, setShowPassword ] = useState(false); + const form = { + priority: useNumberInput("priority", { source: permSub }), + uri: useTextInput("uri", { + source: permSub, + validator: urlValidator, + }), + content_type: useTextInput("content_type", { source: permSub }), + title: useTextInput("title", { source: permSub }), + as_draft: useBoolInput("as_draft", { source: permSub }), + adopt_orphans: useBoolInput("adopt_orphans", { source: permSub }), + useBasicAuth: useBoolInput("useBasicAuth", { + defaultValue: + (permSub.fetch_password !== undefined && permSub.fetch_password !== "") || + (permSub.fetch_username !== undefined && permSub.fetch_username !== ""), + nosubmit: true + }), + fetch_username: useTextInput("fetch_username", { + source: permSub + }), + fetch_password: useTextInput("fetch_password", { + source: permSub + }), + }; + + const [submitUpdate, updateResult] = useFormSubmit( + form, + useUpdateDomainPermissionSubscriptionMutation(), + { + changedOnly: true, + customizeMutationArgs: (mutationData) => { + // Clear username + password if they were set, + // but user has selected to not use basic auth. + if (!form.useBasicAuth.value) { + if (permSub.fetch_username !== undefined && permSub.fetch_username !== "") { + mutationData["fetch_username"] = ""; + } + if (permSub.fetch_password !== undefined && permSub.fetch_password !== "") { + mutationData["fetch_password"] = ""; + } + } + + // Remove useBasicAuth if included. + delete mutationData["useBasicAuth"]; + + // Modify mutation argument to + // include ID and permission type. + return { + id: permSub.id, + permType: permSub.permission_type, + formData: mutationData, + }; + }, + onFinish: res => { + // On a successful response that returns data, + // clear the fetch_username and fetch_password + // fields if they weren't set on the returned sub. + if (res.data) { + if (res.data.fetch_username === undefined || res.data.fetch_username === "") { + form.fetch_username.setter(""); + } + if (res.data.fetch_password === undefined || res.data.fetch_password === "") { + form.fetch_password.setter(""); + } + } + } + } + ); + + const submitDisabled = () => { + // If no basic auth, we don't care what + // fetch_password and fetch_username are. + if (!form.useBasicAuth.value) { + return false; + } + + // Either of fetch_password or fetch_username must be set. + return !(form.fetch_password.value || form.fetch_username.value); + }; + + return ( + <form + className="domain-permission-subscription-update" + onSubmit={submitUpdate} + // Prevent password managers + // trying to fill in fields. + autoComplete="off" + > + <h2>Edit Subscription</h2> + <TextInput + field={form.title} + label={`Subscription title`} + placeholder={`Some List of ${permSub.permission_type === "block" ? "Baddies" : "Goodies"}`} + autoCapitalize="words" + spellCheck="false" + /> + + <NumberInput + field={form.priority} + label={`Subscription priority (0-255)`} + type="number" + min="0" + max="255" + /> + + <TextInput + field={form.uri} + label={`Permission list URL (http or https)`} + placeholder="https://example.org/files/some_list_somewhere" + autoCapitalize="none" + spellCheck="false" + type="url" + /> + + <Select + field={form.content_type} + label="Content type" + options={ + <> + <option value="text/csv">CSV</option> + <option value="application/json">JSON</option> + <option value="text/plain">Plain</option> + </> + } + /> + + <Checkbox + label={ + <> + <>Use </> + <a + href="https://en.wikipedia.org/wiki/Basic_access_authentication" + target="_blank" + rel="noreferrer" + >basic auth</a> + <> when fetching</> + </> + } + field={form.useBasicAuth} + /> + + { form.useBasicAuth.value && + <> + <TextInput + field={form.fetch_username} + label={`Basic auth username`} + autoCapitalize="none" + spellCheck="false" + autoComplete="off" + required={form.useBasicAuth.value && !form.fetch_password.value} + /> + <div className="password-show-hide"> + <TextInput + field={form.fetch_password} + label={`Basic auth password`} + autoCapitalize="none" + spellCheck="false" + type={showPassword ? "" : "password"} + autoComplete="off" + required={form.useBasicAuth.value && !form.fetch_username.value} + /> + <button + className="password-show-hide-toggle" + type="button" + title={!showPassword ? "Show password" : "Hide password"} + onClick={e => { + e.preventDefault(); + setShowPassword(!showPassword); + }} + > + { !showPassword ? "Show" : "Hide" } + </button> + </div> + </> + } + + <Checkbox + label="Adopt orphan permissions" + field={form.adopt_orphans} + /> + + <Checkbox + label="Create permissions as drafts" + field={form.as_draft} + /> + + { !form.as_draft.value && + <div className="info"> + <i className="fa fa-fw fa-exclamation-circle" aria-hidden="true"></i> + <b> + Unchecking "create permissions as drafts" means that permissions found on the + subscribed list will be enforced immediately the next time the list is fetched. + <br/> + If you're subscribing to a block list, this means that blocks will be created + automatically from the given list, potentially severing any existing follow + relationships with accounts on the blocked domain. + <br/> + Before saving, make sure this is what you really want to do, and consider + creating domain excludes for domains that you want to manage manually. + </b> + </div> + } + + <MutationButton + label="Save" + result={updateResult} + disabled={submitDisabled()} + /> + + </form> + ); +} + +function DeleteDomainPermSub({ permSub, backLocation }: { permSub: DomainPermSub, backLocation: string }) { + const permType = permSub.permission_type; + if (!permType) { + throw "permission_type was undefined"; + } + + const [_location, setLocation] = useLocation(); + const [ removeSub, result ] = useRemoveDomainPermissionSubscriptionMutation(); + const removeChildren = useBoolInput("remove_children", { defaultValue: false }); + + return ( + <form className="domain-permission-subscription-remove"> + <h2>Remove Subscription</h2> + + <Checkbox + label={`Also remove any ${permType}s created by this subscription`} + field={removeChildren} + /> + + <MutationButton + label={`Remove`} + title={`Remove`} + type="button" + className="button danger" + onClick={(e) => { + e.preventDefault(); + const id = permSub.id; + const remove_children = removeChildren.value as boolean; + removeSub({ id, remove_children }).then(res => { + if ("data" in res) { + setLocation(backLocation); + } + }); + }} + disabled={false} + showError={true} + result={result} + /> + </form> + ); +} |