Merge branch 'admin-tabs-2' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2025-12-08 22:31:37 +02:00
commit 12b98a48b2
71 changed files with 4150 additions and 540 deletions

View file

@ -0,0 +1 @@
Most of the remaining AdminFE tabs into Admin Dashboard

View file

@ -513,6 +513,12 @@ nav {
} }
} }
label {
&.-disabled {
color: var(--textFaint);
}
}
input, input,
textarea { textarea {
border: none; border: none;
@ -553,6 +559,10 @@ textarea {
&[disabled="disabled"], &[disabled="disabled"],
&.disabled { &.disabled {
cursor: not-allowed; cursor: not-allowed;
color: var(--textFaint);
/* stylelint-disable-next-line declaration-no-important */
background-color: transparent !important;
} }
&[type="range"] { &[type="range"] {
@ -578,6 +588,8 @@ textarea {
& + label::before { & + label::before {
opacity: 0.5; opacity: 0.5;
} }
background-color: var(--background);
} }
+ label::before { + label::before {
@ -677,7 +689,8 @@ option {
list-style: none; list-style: none;
display: grid; display: grid;
grid-auto-flow: row dense; grid-auto-flow: row dense;
grid-template-columns: 1fr 1fr; grid-template-columns: repeat(auto-fit, minmax(20em, 1fr));
grid-gap: 0.5em;
li { li {
border: 1px solid var(--border); border: 1px solid var(--border);
@ -692,13 +705,16 @@ option {
display: inline-flex; display: inline-flex;
vertical-align: middle; vertical-align: middle;
.Select select {
line-height: 1;
}
> *, > *,
> * .button-default { > * .button-default {
--_roundness-left: 0; --_roundness-left: 0;
--_roundness-right: 0; --_roundness-right: 0;
position: relative; position: relative;
flex: 1 1 auto;
} }
> *:first-child, > *:first-child,

View file

@ -1,7 +1,7 @@
<template> <template>
<label <label
class="checkbox" class="checkbox"
:class="[{ disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }, radio ? '-radio' : '-checkbox']" :class="[{ ['-disabled']: disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }, radio ? '-radio' : '-checkbox']"
> >
<span <span
v-if="!!$slots.before" v-if="!!$slots.before"
@ -123,7 +123,7 @@ export default {
.disabled { .disabled {
.checkbox-indicator::before { .checkbox-indicator::before {
background-color: var(--background); background-color: transparent;
} }
} }

View file

@ -395,7 +395,7 @@
} }
form textarea { form textarea {
line-height: 16px; line-height: 1;
resize: vertical; resize: vertical;
} }

View file

@ -26,6 +26,7 @@
/* TODO fix order of styles */ /* TODO fix order of styles */
label.Select { label.Select {
padding: 0; padding: 0;
display: flex;
select { select {
appearance: none; appearance: none;
@ -33,13 +34,12 @@ label.Select {
border: none; border: none;
color: var(--text); color: var(--text);
margin: 0; margin: 0;
padding: 0 2em 0 0.2em; padding: 0 2em 0 0.5em;
font-family: var(--font); font-family: var(--font);
font-size: 1em; font-size: 1em;
width: 100%; width: 100%;
z-index: 1; z-index: 1;
height: 2em; line-height: 2;
line-height: 16px;
&[multiple], &[multiple],
&[size] { &[size] {
@ -79,7 +79,7 @@ label.Select {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
right: 5px; right: 0.5em;
height: 100%; height: 100%;
width: 0.875em; width: 0.875em;
font-family: var(--font); font-family: var(--font);

View file

@ -0,0 +1,39 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const AuthTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
TupleSetting,
AttachmentSetting,
GroupSetting,
ListSetting,
MapSetting
},
computed: {
...SharedComputedObject(),
LDAPEnabled () {
return this.$store.state.adminSettings.draft[':pleroma'][':ldap'][':enabled']
},
}
}
export default AuthTab

View file

@ -0,0 +1,105 @@
<template>
<div :label="$t('admin_dash.tabs.job_queues')">
<div class="setting-item">
<h3>{{ $t('admin_dash.auth.MFA') }}</h3>
<ul class="setting-list">
<li>
<h4>{{ $t('admin_dash.auth.TOTP') }}</h4>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:totp.:digits" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:totp.:period" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.auth.backup_codes') }}</h4>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:backup_codes.:number" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:multi_factor_authentication.:backup_codes.:length" />
</li>
</ul>
</li>
<GroupSetting path=":pleroma.:instance.:multi_factor_authentication" />
</ul>
<h3>{{ $t('admin_dash.auth.OAuth') }}</h3>
<ul class="setting-list">
<li>
<StringSetting path=":pleroma.:auth.:auth_template" />
</li>
<li>
<BooleanSetting path=":pleroma.:auth.:enforce_oauth_admin_scope_usage" />
</li>
<li>
<StringSetting path=":pleroma.:auth.:oauth_consumer_template" />
</li>
<li>
<ListSetting path=":pleroma.:auth.:oauth_consumer_strategies" />
</li>
<li>
<IntegerSetting path=":pleroma.:oauth2.:token_expires_in" />
</li>
<li>
<BooleanSetting path=":pleroma.:oauth2.:issue_new_refresh_token" />
</li>
<li>
<BooleanSetting path=":pleroma.:oauth2.:clean_expired_tokens" />
</li>
</ul>
<h3>{{ $t('admin_dash.auth.LDAP') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:ldap.:enabled" />
</li>
<template v-if="LDAPEnabled">
<li>
<StringSetting path=":pleroma.:ldap.:host" />
</li>
<li>
<IntegerSetting path=":pleroma.:ldap.:port" />
</li>
<li>
<BooleanSetting path=":pleroma.:ldap.:tls" />
</li>
<li>
<!-- CONFIRM old admin FE only supports ONE setting which is Verify, is that correct or should we allow more than one? -->
<MapSetting
:allow-new="false"
path=":pleroma.:ldap.:tlsopts"
/>
</li>
<li>
<BooleanSetting path=":pleroma.:ldap.:ssl" />
</li>
<li>
<!-- CONFIRM old admin FE only supports ONE setting which is Verify, is that correct or should we allow more than one? -->
<MapSetting
:allow-new="false"
path=":pleroma.:ldap.:sslopts"
/>
</li>
<li>
<StringSetting path=":pleroma.:ldap.:base" />
</li>
<li>
<StringSetting path=":pleroma.:ldap.:uid" />
</li>
<li>
<StringSetting path=":pleroma.:ldap.:cacertfile" />
</li>
<li>
<StringSetting path=":pleroma.:ldap.:mail" />
</li>
</template>
</ul>
</div>
<!-- CONFIRM admin token is present in AdminFE but missing in both data and descriptions?? -->
</div>
</template>
<script src="./auth_tab.js"></script>

View file

@ -10,6 +10,22 @@ import ModifiedIndicator from '../helpers/modified_indicator.vue'
import EmojiEditingPopover from '../helpers/emoji_editing_popover.vue' import EmojiEditingPopover from '../helpers/emoji_editing_popover.vue'
import { useInterfaceStore } from 'src/stores/interface' import { useInterfaceStore } from 'src/stores/interface'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faArrowsRotate,
faFolderOpen,
faServer,
faDownload
} from '@fortawesome/free-solid-svg-icons'
library.add(
faArrowsRotate,
faFolderOpen,
faDownload,
faServer
)
const EmojiTab = { const EmojiTab = {
components: { components: {
TabSwitcher, TabSwitcher,
@ -44,10 +60,12 @@ const EmojiTab = {
}, },
computed: { computed: {
...SharedComputedObject(),
pack () { pack () {
return this.packName !== '' ? this.knownPacks[this.packName] : undefined return this.packName !== '' ? this.knownPacks[this.packName] : undefined
}, },
packMeta () { packMeta () {
if (this.packName === '') return {}
if (this.editedMetadata[this.packName] === undefined) { if (this.editedMetadata[this.packName] === undefined) {
this.editedMetadata[this.packName] = clone(this.pack.pack) this.editedMetadata[this.packName] = clone(this.pack.pack)
} }
@ -98,8 +116,6 @@ const EmojiTab = {
return Promise.reject(resp) return Promise.reject(resp)
} }
}).then(() => { }).then(() => {
this.$refs.createPackPopover.hidePopover()
this.packName = this.newPackName this.packName = this.newPackName
this.newPackName = '' this.newPackName = ''
}) })
@ -205,8 +221,6 @@ const EmojiTab = {
for (const pack in this.knownRemotePacks[inst]) { for (const pack in this.knownRemotePacks[inst]) {
this.sortPackFiles(`${pack}@${inst}`) this.sortPackFiles(`${pack}@${inst}`)
} }
this.$refs.remotePackPopover.hidePopover()
}) })
.catch(data => { .catch(data => {
this.displayError(data) this.displayError(data)
@ -223,8 +237,6 @@ const EmojiTab = {
.then(data => data.json()) .then(data => data.json())
.then(resp => { .then(resp => {
if (resp === 'ok') { if (resp === 'ok') {
this.$refs.downloadPackPopover.hidePopover()
return this.refreshPackList() return this.refreshPackList()
} else { } else {
this.displayError(resp.error) this.displayError(resp.error)
@ -242,8 +254,6 @@ const EmojiTab = {
.then(data => data.json()) .then(data => data.json())
.then(resp => { .then(resp => {
if (resp === 'ok') { if (resp === 'ok') {
this.$refs.additionalRemotePopover.hidePopover()
return this.refreshPackList() return this.refreshPackList()
} else { } else {
this.displayError(resp.error) this.displayError(resp.error)
@ -262,8 +272,6 @@ const EmojiTab = {
.then(data => data.json()) .then(data => data.json())
.then(resp => { .then(resp => {
if (resp === 'ok') { if (resp === 'ok') {
this.$refs.additionalRemotePopover.hidePopover()
return this.refreshPackList() return this.refreshPackList()
} else { } else {
this.displayError(resp.error) this.displayError(resp.error)

View file

@ -1,10 +1,48 @@
.emoji-tab { .EmojiTab {
.btn-group .btn:not(:first-child) { .setting-list {
margin-left: 0.5em; padding-left: 0.75em;
} }
.pack-info-wrapper { .toolbar {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
.header-buttons {
display: flex;
flex: 1 0 auto;
justify-content: end;
&:not(.btn-group) {
gap: 0.5em;
}
.header-text {
flex: 1 0 auto;
}
}
.button-default {
flex: 0 0 auto;
padding: 0.5em;
font-size: 1rem;
}
}
.selector-buttons,
.meta-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
}
h5 {
margin-top: 1em; margin-top: 1em;
margin-bottom: 0.25em;
}
h3.toolbar {
align-items: end;
} }
.emoji-info-input { .emoji-info-input {
@ -18,8 +56,8 @@
} }
.emoji { .emoji {
width: 32px; width: 2em;
height: 32px; height: 2em;
} }
.emoji-unsaved { .emoji-unsaved {
@ -30,6 +68,14 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 1em; gap: 1em;
.placeholder {
background: var(--textFaint);
border-radius: 0.5em;
width: 2em;
height: 2em;
opacity: 0.5;
}
} }
} }

View file

@ -1,168 +1,75 @@
<template> <template>
<div <div
class="emoji-tab" class="EmojiTab"
:label="$t('admin_dash.tabs.emoji')" :label="$t('admin_dash.tabs.emoji')"
> >
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('admin_dash.tabs.emoji') }}</h2> <h3 class="toolbar">
<span class="header-text">
{{ $t('admin_dash.emoji.emoji_packs') }}
</span>
<ul class="setting-list"> <span class="header-buttons btn-group">
<h3>{{ $t('admin_dash.emoji.global_actions') }}</h3>
<li class="btn-group setting-item">
<button <button
class="button button-default btn" class="button button-default"
type="button" type="button"
:title="$t('admin_dash.emoji.reload')"
@click="reloadEmoji" @click="reloadEmoji"
> >
{{ $t('admin_dash.emoji.reload') }} <FAIcon icon="arrows-rotate" />
</button> {{ $t('admin_dash.emoji.reload_short') }}
<button
class="button button-default btn"
type="button"
@click="importFromFS"
>
{{ $t('admin_dash.emoji.importFS') }}
</button>
</li>
<li class="btn-group setting-item">
<button
class="button button-default btn"
type="button"
@click="$refs.remotePackPopover.showPopover"
>
{{ $t('admin_dash.emoji.remote_packs') }}
<Popover
ref="remotePackPopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.remote_pack_instance') }}</h3>
<input
v-model="remotePackInstance"
class="input"
:placeholder="$t('admin_dash.emoji.remote_pack_instance')"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="listRemotePacks"
>
{{ $t('admin_dash.emoji.do_list') }}
</button>
</div>
</template>
</Popover>
</button>
<button
class="button button-default emoji-panel-additional-actions"
@click="$refs.additionalRemotePopover.showPopover"
>
<FAIcon
icon="chevron-down"
/>
<Popover
ref="additionalRemotePopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
<input
v-model="newPackName"
:placeholder="$t('admin_dash.emoji.new_pack_name')"
class="input"
>
<h3>Import pack from URL</h3>
<input
v-model="remotePackURL"
class="input"
placeholder="Pack .zip URL"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackURL.trim() === ''"
@click="downloadRemoteURLPack"
>
Import
</button>
<h3>Import pack from a file</h3>
<input
type="file"
accept="application/zip"
class="emoji-tab-popover-file input"
@change="remotePackFile = $event.target.files"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackFile === null || remotePackFile.length === 0"
@click="downloadRemoteFilePack"
>
Import
</button>
</div>
</template>
</Popover>
</button>
</li>
<h3>{{ $t('admin_dash.emoji.emoji_packs') }}</h3>
<li>
<h4>{{ $t('admin_dash.emoji.edit_pack') }}</h4>
<Select
v-model="packName"
class="form-control"
>
<option
value=""
disabled
hidden
>
{{ $t('admin_dash.emoji.emoji_pack') }}
</option>
<option
v-for="(pack, listPackName) in knownPacks"
:key="listPackName"
:label="listPackName"
>
{{ listPackName }}
</option>
</Select>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="$refs.createPackPopover.showPopover"
>
{{ $t('admin_dash.emoji.create_pack') }}
</button> </button>
<Popover <Popover
ref="createPackPopover"
popover-class="emoji-tab-edit-popover popover-default" popover-class="emoji-tab-edit-popover popover-default"
trigger="click" trigger="click"
placement="bottom" placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
> >
<template #trigger>
<button
class="button button-default"
type="button"
:title="$t('admin_dash.emoji.remote_packs')"
>
<FAIcon icon="download" />
{{ $t('admin_dash.emoji.remote_packs_short') }}
</button>
</template>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.remote_pack_instance') }}</h3>
<input
v-model="remotePackInstance"
class="input"
:placeholder="$t('admin_dash.emoji.remote_pack_instance')"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="listRemotePacks"
>
{{ $t('admin_dash.emoji.do_list') }}
</button>
</div>
</template>
</Popover>
<Popover
ref="additionalRemotePopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
>
<template #trigger>
<button
class="button button-default emoji-panel-additional-actions"
:title="$t('admin_dash.emoji.import_pack')"
@click="$refs.additionalRemotePopover.showPopover"
>
<FAIcon icon="folder-open" />
{{ $t('admin_dash.emoji.import_pack_short') }}
</button>
</template>
<template #content> <template #content>
<div class="emoji-tab-popover-input"> <div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3> <h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
@ -171,94 +78,252 @@
:placeholder="$t('admin_dash.emoji.new_pack_name')" :placeholder="$t('admin_dash.emoji.new_pack_name')"
class="input" class="input"
> >
<h3>Import pack from URL</h3>
<input
v-model="remotePackURL"
class="input"
placeholder="Pack .zip URL"
>
<button <button
class="button button-default btn emoji-tab-popover-button" class="button button-default btn emoji-tab-popover-button"
type="button" type="button"
@click="createEmojiPack" :disabled="newPackName.trim() === '' || remotePackURL.trim() === ''"
@click="downloadRemoteURLPack"
> >
{{ $t('admin_dash.emoji.create') }} Import
</button>
<h3>Import pack from a file</h3>
<input
type="file"
accept="application/zip"
class="emoji-tab-popover-file input"
@change="remotePackFile = $event.target.files"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackFile === null || remotePackFile.length === 0"
@click="downloadRemoteFilePack"
>
Import
</button> </button>
</div> </div>
</template> </template>
</Popover> </Popover>
</li> </span>
</ul> </h3>
<div class="setting-list">
<h4 class="toolbar">
{{ $t('admin_dash.emoji.edit_pack') }}
</h4>
<div class="selector-buttons setting-list">
<button
:disabled="!pack || pack.remote !== undefined"
class="button button-default btn"
type="button"
@click="deleteModalVisible = true"
>
{{ $t('admin_dash.emoji.delete_pack') }}
<div v-if="pack"> <ConfirmModal
<div class="pack-info-wrapper"> v-if="deleteModalVisible"
<ul class="setting-list"> :title="$t('admin_dash.emoji.delete_title')"
<li> :cancel-text="$t('status.delete_confirm_cancel_button')"
<label> :confirm-text="$t('status.delete_confirm_accept_button')"
{{ $t('admin_dash.emoji.description') }} @cancelled="deleteModalVisible = false"
<ModifiedIndicator @accepted="deleteEmojiPack"
:changed="metaEdited('description')" >
message-key="admin_dash.emoji.metadata_changed" {{ $t('admin_dash.emoji.delete_confirm', [packName]) }}
/> </ConfirmModal>
</button>
<textarea <button
v-model="packMeta.description" :disabled="!pack || pack.remote === undefined"
:disabled="pack.remote !== undefined" class="button button-default btn"
class="bio resize-height input" type="button"
/> @click="$refs.downloadPackPopover.showPopover"
</label> >
</li> {{ $t('admin_dash.emoji.download_pack') }}
<li>
<label>
{{ $t('admin_dash.emoji.homepage') }}
<ModifiedIndicator
:changed="metaEdited('homepage')"
message-key="admin_dash.emoji.metadata_changed"
/>
<input <Popover
v-model="packMeta.homepage" ref="downloadPackPopover"
class="emoji-info-input input" trigger="click"
:disabled="pack.remote !== undefined" placement="bottom"
> bound-to-selector=".emoji-tab"
</label> popover-class="emoji-tab-edit-popover popover-default"
</li> :bound-to="{ x: 'container' }"
<li> :offset="{ y: 5 }"
<label> >
{{ $t('admin_dash.emoji.fallback_src') }} <template #content>
<ModifiedIndicator <h3>{{ $t('admin_dash.emoji.downloading_pack', [packName]) }}</h3>
:changed="metaEdited('fallback-src')" <div>
message-key="admin_dash.emoji.metadata_changed" <div>
/> <div class="emoji-tab-popover-input">
<label>
{{ $t('admin_dash.emoji.download_as_name') }}
<input
v-model="remotePackDownloadAs"
class="emoji-data-input input"
:placeholder="$t('admin_dash.emoji.download_as_name_full')"
>
</label>
<input <div
v-model="packMeta['fallback-src']" v-if="downloadWillReplaceLocal"
class="emoji-info-input input" class="warning"
:disabled="pack.remote !== undefined" >
> <em>{{ $t('admin_dash.emoji.replace_warning') }}</em>
</label> </div>
</li> </div>
<li>
<label>
{{ $t('admin_dash.emoji.fallback_sha256') }}
<input <button
v-model="packMeta['fallback-src-sha256']" class="button button-default btn"
:disabled="true" type="button"
class="emoji-info-input input" @click="downloadRemotePack"
> >
</label> {{ $t('admin_dash.emoji.download') }}
</li> </button>
<li> </div>
<Checkbox </div>
v-model="packMeta['share-files']" </template>
:disabled="pack.remote !== undefined" </Popover>
</button>
<span class="btn-group">
<Select
v-model="packName"
class="form-control"
>
<option
value=""
disabled
hidden
> >
{{ $t('admin_dash.emoji.share') }} {{ $t('admin_dash.emoji.emoji_pack') }}
</Checkbox> </option>
<option
v-for="(pack, listPackName) in knownPacks"
:key="listPackName"
:label="listPackName"
>
{{ listPackName }}
</option>
</Select>
<Popover
ref="createPackPopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
>
<template #trigger>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
>
{{ $t('admin_dash.emoji.create_pack') }}
</button>
</template>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
<input
v-model="newPackName"
:placeholder="$t('admin_dash.emoji.new_pack_name')"
class="input"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
@click="createEmojiPack"
>
{{ $t('admin_dash.emoji.create') }}
</button>
</div>
</template>
</Popover>
</span>
</div>
<h5>
{{ $t('admin_dash.emoji.metadata') }}
<ModifiedIndicator
:changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
message-key="admin_dash.emoji.emoji_changed"
/>
</h5>
<ul class="setting-list">
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.description') }}
<ModifiedIndicator <ModifiedIndicator
:changed="metaEdited('share-files')" :changed="metaEdited('description')"
message-key="admin_dash.emoji.metadata_changed" message-key="admin_dash.emoji.metadata_changed"
/> />
</li>
<li class="btn-group"> <textarea
v-model="packMeta.description"
:disabled="!pack || pack.remote !== undefined"
class="bio resize-height input"
/>
</label>
</li>
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.homepage') }}
<ModifiedIndicator
:changed="metaEdited('homepage')"
message-key="admin_dash.emoji.metadata_changed"
/>
<input
v-model="packMeta.homepage"
class="emoji-info-input input"
:disabled="!pack || pack.remote !== undefined"
>
</label>
</li>
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.fallback_src') }}
<ModifiedIndicator
:changed="metaEdited('fallback-src')"
message-key="admin_dash.emoji.metadata_changed"
/>
<input
v-model="packMeta['fallback-src']"
class="emoji-info-input input"
:disabled="!pack || pack.remote !== undefined"
>
</label>
</li>
<li>
<label :class="{ ['-disabled']: !pack || pack.remote !== undefined }">
{{ $t('admin_dash.emoji.fallback_sha256') }}
<input
v-model="packMeta['fallback-src-sha256']"
:disabled="!pack || pack.remote !== undefined"
class="emoji-info-input input"
>
</label>
</li>
<li>
<Checkbox
v-model="packMeta['share-files']"
:disabled="!pack || pack.remote !== undefined"
>
{{ $t('admin_dash.emoji.share') }}
</Checkbox>
<ModifiedIndicator
:changed="metaEdited('share-files')"
message-key="admin_dash.emoji.metadata_changed"
/>
<div class="meta-buttons">
<button <button
v-if="pack.remote === undefined" v-if="pack && pack.remote === undefined"
class="button button-default btn" class="button button-default btn"
type="button" type="button"
@click="savePackMetadata" @click="savePackMetadata"
@ -266,148 +331,88 @@
{{ $t('admin_dash.emoji.save_meta') }} {{ $t('admin_dash.emoji.save_meta') }}
</button> </button>
<button <button
v-if="pack.remote === undefined" v-if="pack && pack.remote === undefined"
class="button button-default btn" class="button button-default btn"
type="button" type="button"
@click="savePackMetadata" @click="savePackMetadata"
> >
{{ $t('admin_dash.emoji.revert_meta') }} {{ $t('admin_dash.emoji.revert_meta') }}
</button> </button>
</div>
<button </li>
v-if="pack.remote === undefined"
class="button button-default btn"
type="button"
@click="deleteModalVisible = true"
>
{{ $t('admin_dash.emoji.delete_pack') }}
<ConfirmModal
v-if="deleteModalVisible"
:title="$t('admin_dash.emoji.delete_title')"
:cancel-text="$t('status.delete_confirm_cancel_button')"
:confirm-text="$t('status.delete_confirm_accept_button')"
@cancelled="deleteModalVisible = false"
@accepted="deleteEmojiPack"
>
{{ $t('admin_dash.emoji.delete_confirm', [packName]) }}
</ConfirmModal>
</button>
<button
v-if="pack.remote !== undefined"
class="button button-default btn"
type="button"
@click="$refs.downloadPackPopover.showPopover"
>
{{ $t('admin_dash.emoji.download_pack') }}
<Popover
ref="downloadPackPopover"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
popover-class="emoji-tab-edit-popover popover-default"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<h3>{{ $t('admin_dash.emoji.downloading_pack', [packName]) }}</h3>
<div>
<div>
<div class="emoji-tab-popover-input">
<label>
{{ $t('admin_dash.emoji.download_as_name') }}
<input
v-model="remotePackDownloadAs"
class="emoji-data-input input"
:placeholder="$t('admin_dash.emoji.download_as_name_full')"
>
</label>
<div
v-if="downloadWillReplaceLocal"
class="warning"
>
<em>{{ $t('admin_dash.emoji.replace_warning') }}</em>
</div>
</div>
<button
class="button button-default btn"
type="button"
@click="downloadRemotePack"
>
{{ $t('admin_dash.emoji.download') }}
</button>
</div>
</div>
</template>
</Popover>
</button>
</li>
</ul>
</div>
<ul class="setting-list">
<h4>
{{ $t('admin_dash.emoji.files') }}
<ModifiedIndicator
v-if="pack"
:changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
message-key="admin_dash.emoji.emoji_changed"
/>
</h4>
<div
v-if="pack"
class="emoji-list"
>
<EmojiEditingPopover
v-if="pack.remote === undefined"
placement="bottom"
new-upload
:title="$t('admin_dash.emoji.adding_new')"
:pack-name="packName"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<FAIcon
icon="plus"
size="2x"
:title="$t('admin_dash.emoji.add_file')"
/>
</template>
</EmojiEditingPopover>
<EmojiEditingPopover
v-for="(file, shortcode) in pack.files"
ref="emojiPopovers"
:key="shortcode"
placement="top"
:title="$t(`admin_dash.emoji.${pack.remote === undefined ? 'editing' : 'copying'}`, [shortcode])"
:shortcode="shortcode"
:file="file"
:pack-name="packName"
:remote="pack.remote"
:known-local-packs="knownLocalPacks"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<StillImage
class="emoji"
:src="emojiAddr(file)"
:title="`:${shortcode}:`"
:alt="`:${shortcode}:`"
/>
</template>
</EmojiEditingPopover>
</div>
</ul> </ul>
<h5>
{{ $t('admin_dash.emoji.files') }}
<ModifiedIndicator
:changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)"
message-key="admin_dash.emoji.emoji_changed"
/>
</h5>
<div
class="emoji-list setting-list"
>
<EmojiEditingPopover
v-if="pack && pack.remote === undefined"
placement="bottom"
new-upload
:title="$t('admin_dash.emoji.adding_new')"
:pack-name="packName"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<FAIcon
icon="plus"
size="2x"
:title="$t('admin_dash.emoji.add_file')"
/>
</template>
</EmojiEditingPopover>
<template v-if="!pack">
<div
v-for="(_, i) in new Array(20)"
:key="i"
class="placeholder"
/>
</template>
<EmojiEditingPopover
v-for="(file, shortcode) in (pack?.files || [])"
ref="emojiPopovers"
:key="shortcode"
placement="top"
:title="$t(`admin_dash.emoji.${pack?.remote === undefined ? 'editing' : 'copying'}`, [shortcode])"
:shortcode="shortcode"
:file="file"
:pack-name="packName"
:remote="pack?.remote"
:known-local-packs="knownLocalPacks"
@update-pack-files="updatePackFiles"
@display-error="displayError"
>
<template #trigger>
<StillImage
class="emoji"
:src="emojiAddr(file)"
:title="`:${shortcode}:`"
:alt="`:${shortcode}:`"
/>
</template>
</EmojiEditingPopover>
</div>
</div> </div>
<h3>{{ $t('admin_dash.emoji.advanced') }}</h3>
<button
class="button button-default btn"
type="button"
@click="importFromFS"
>
<FAIcon icon="server" />
{{ $t('admin_dash.emoji.importFS') }}
</button>
</div> </div>
</div> </div>
</template> </template>

View file

@ -0,0 +1,34 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import ListTupleSetting from '../helpers/list_tuple_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const FederationTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
ListSetting,
ListTupleSetting,
GroupSetting,
MapSetting
},
computed: {
...SharedComputedObject()
}
}
export default FederationTab

View file

@ -0,0 +1,68 @@
<template>
<div :label="$t('admin_dash.tabs.federation')">
<div class="setting-item">
<h3>{{ $t('admin_dash.federation.global') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:federating" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:skip_thread_containment" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:external_user_synchronization" />
</li>
</ul>
<h3>{{ $t('admin_dash.federation.restrictions') }}</h3>
<ul class="setting-list">
<li>
<MapSetting path=":pleroma.:instance.:quarantined_instances" />
</li>
<li>
<MapSetting path=":pleroma.:instance.:rejected_instances" />
</li>
</ul>
<h3>{{ $t('admin_dash.federation.activitypub') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:allow_relay" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:unfollow_blocked" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:outgoing_blocks" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:blockers_visible" />
</li>
<li>
<IntegerSetting path=":pleroma.:activitypub.:follow_handshake_timeout" />
</li>
<li>
<IntegerSetting path=":pleroma.:activitypub.:note_replies_output_limit" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:federation_incoming_replies_max_depth" />
</li>
<li>
<IntegerSetting path=":pleroma.:instance.:federation_reachability_timeout_days" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:allow_relay" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:sign_object_fetches" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:authorized_fetch_mode" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:client_api_enabled" />
</li>
</ul>
</div>
</div>
</template>
<script src="./federation_tab.js"></script>

View file

@ -44,10 +44,10 @@ const FrontendsTab = {
} }
}, },
computed: { computed: {
...SharedComputedObject(),
frontends () { frontends () {
return this.$store.state.adminSettings.frontends return this.$store.state.adminSettings.frontends
}, }
...SharedComputedObject()
}, },
methods: { methods: {
canInstall (frontend) { canInstall (frontend) {

View file

@ -1,8 +1,24 @@
.frontends-tab { .FrontendsTab {
.cards-list { .cards-list {
padding: 0; padding: 0;
} }
li.frontend-card {
display: flex;
margin: 0;
flex-direction: column;
}
.frontend-buttons {
margin-top: 0.5em;
display: flex;
justify-content: end;
gap: 0.5em;
flex-wrap: wrap;
flex: 1 0 auto;
align-items: end;
}
.relative { .relative {
position: relative; position: relative;
} }
@ -16,10 +32,26 @@
inset: 0; inset: 0;
} }
h5 {
margin: 0;
font-size: 1.15em
}
dl {
margin-left: 1em;
}
dt {
margin-top: 0.5em;
text-overflow: ellipsis;
white-space: nowrap;
overflow-x: hidden;
}
dd { dd {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow-x: hidden; overflow-x: hidden;
max-width: 10em;
} }
} }

View file

@ -1,17 +1,17 @@
<template> <template>
<div <div
class="frontends-tab" class="FrontendsTab"
:label="$t('admin_dash.tabs.frontends')" :label="$t('admin_dash.tabs.frontends')"
> >
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('admin_dash.tabs.frontends') }}</h2> <h3>{{ $t('admin_dash.frontend.title') }}</h3>
<p>{{ $t('admin_dash.frontend.wip_notice') }}</p> <p>{{ $t('admin_dash.frontend.wip_notice') }}</p>
<ul <ul
v-if="adminDraft" v-if="adminDraft"
class="setting-list" class="setting-list"
> >
<li> <li>
<h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3> <h4>{{ $t('admin_dash.frontend.default_frontend') }}</h4>
<p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p> <p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
@ -38,13 +38,14 @@
v-if="working" v-if="working"
class="overlay" class="overlay"
/> />
<h3>{{ $t('admin_dash.frontend.available_frontends') }}</h3> <h4>{{ $t('admin_dash.frontend.available_frontends') }}</h4>
<ul class="cards-list"> <ul class="cards-list">
<li <li
v-for="frontend in frontends" v-for="frontend in frontends"
:key="frontend.name" :key="frontend.name"
class="frontend-card"
> >
<strong>{{ frontend.name }}</strong> <h5>{{ frontend.name }}</h5>
{{ ' ' }} {{ ' ' }}
<span v-if="adminDraft && adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name"> <span v-if="adminDraft && adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name">
<i18n-t <i18n-t
@ -89,7 +90,7 @@
>{{ frontend.build_url }}</a> >{{ frontend.build_url }}</a>
</dd> </dd>
</dl> </dl>
<div> <div class="frontend-buttons">
<span class="btn-group"> <span class="btn-group">
<button <button
class="button button-default btn" class="button button-default btn"

View file

@ -0,0 +1,46 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import ProxySetting from '../helpers/proxy_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { get } from 'lodash'
const HTTPTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
MapSetting,
GroupSetting,
ListSetting,
TupleSetting,
ProxySetting
},
computed: {
...SharedComputedObject(),
sslOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:http.:adapter.:ssl_options.:versions')
return new Set(desc.suggestions.map(option => ({
label: option.replace(':tlsv', 'TLS v'),
value: option
})))
},
}
}
export default HTTPTab

View file

@ -0,0 +1,126 @@
<template>
<div
class="LinksTab"
:label="$t('admin_dash.tabs.http')"
>
<div class="setting-item">
<h3>{{ $t('admin_dash.http.outbound') }}</h3>
<ul class="setting-list">
<li>
<ProxySetting
hide-description
path=":pleroma.:http.:proxy_url"
/>
</li>
<li>
<BooleanSetting path=":pleroma.:http.:send_user_agent" />
</li>
<li>
<StringSetting path=":pleroma.:http.:user_agent" />
</li>
<li>
<ListSetting
override-available-options
:options="sslOptions"
path=":pleroma.:http.:adapter.:ssl_options.:versions"
/>
</li>
<li>
<GroupSetting path=":pleroma.:http.:adapter" />
</li>
</ul>
</div>
<div class="setting-item">
<h3>{{ $t('admin_dash.http.incoming') }}</h3>
<ul class="setting-list">
<h4>{{ $t('admin_dash.http.cors') }}</h4>
<li>
<IntegerSetting
:description-path-override="[':cors_plug', '<ROOT>', ':max_age']"
path=":cors_plug.:max_age"
/>
</li>
<li>
<BooleanSetting
:description-path-override="[':cors_plug', '<ROOT>', ':credentials']"
path=":cors_plug.:credentials"
/>
</li>
<li>
<ListSetting
path=":cors_plug.:methods"
:description-path-override="[':cors_plug', '<ROOT>', ':methods']"
/>
</li>
<li>
<ListSetting
:description-path-override="[':cors_plug', '<ROOT>', ':expose']"
path=":cors_plug.:expose"
/>
</li>
<li>
<ListSetting
:description-path-override="[':cors_plug', '<ROOT>', ':headers']"
path=":cors_plug.:headers"
/>
</li>
<h4>{{ $t('admin_dash.http.security') }}</h4>
<li>
<BooleanSetting path=":pleroma.:http_security.:enabled" />
</li>
<li>
<BooleanSetting path=":pleroma.:http_security.:sts" />
</li>
<li>
<IntegerSetting path=":pleroma.:http_security.:sts_max_age" />
</li>
<li>
<IntegerSetting path=":pleroma.:http_security.:ct_max_age" />
</li>
<li>
<StringSetting path=":pleroma.:http_security.:referrer_policy" />
</li>
<li>
<BooleanSetting path=":pleroma.:http_security.:allow_unsafe_eval" />
</li>
<li>
<StringSetting path=":pleroma.:http_security.:report_url" />
</li>
<h4>{{ $t('admin_dash.http.web_cache_ttl') }}</h4>
<p>{{ $t('admin_dash.http.web_cache_ttl_description') }}</p>
<li>
<StringSetting path=":pleroma.:web_cache_ttl.:activity_pub" />
</li>
<li>
<StringSetting path=":pleroma.:web_cache_ttl.:activity_pub_question" />
</li>
</ul>
<h3>{{ $t('admin_dash.http.web_push') }}</h3>
<p>{{ $t('admin_dash.http.web_push_description') }}</p>
<ul class="setting-list">
<li>
<StringSetting
path=":web_push_encryption.:vapid_details.:subject"
/>
</li>
<li>
<StringSetting
path=":web_push_encryption.:vapid_details.:public_key"
/>
</li>
<li>
<StringSetting
path=":web_push_encryption.:vapid_details.:private_key"
/>
</li>
</ul>
<!-- CONFIRM admin_token should go there but something is wrong with both data and description. -->
<!-- given the nature of the setting it's probably better to not expose it and deprecate it on backend side -->
<!-- CONFIRM :pleroma.:streamer should also PROBABLY? go here but it's completely MIA from backend besides references to config description -->
</div>
</div>
</template>
<!--<style lang="scss" src="./http_tab.scss"></style>-->
<script src="./http_tab.js"></script>

View file

@ -2,18 +2,15 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue' import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue' import StringSetting from '../helpers/string_setting.vue'
import ColorSetting from '../helpers/color_setting.vue'
import GroupSetting from '../helpers/group_setting.vue' import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue' import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import PWAManifestIconsSetting from '../helpers/pwa_manifest_icons_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js' import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { get } from 'lodash'
import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faGlobe
)
const InstanceTab = { const InstanceTab = {
provide () { provide () {
@ -27,11 +24,29 @@ const InstanceTab = {
ChoiceSetting, ChoiceSetting,
IntegerSetting, IntegerSetting,
StringSetting, StringSetting,
ColorSetting,
AttachmentSetting, AttachmentSetting,
ListSetting,
PWAManifestIconsSetting,
MapSetting,
GroupSetting GroupSetting
}, },
computed: { computed: {
...SharedComputedObject() ...SharedComputedObject(),
providersOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Web.Metadata', ':providers'])
return new Set(desc.suggestions.map(option => ({
label: option.replace('Pleroma.Web.Metadata.Providers.', ''),
value: option
})))
},
limitLocalContentOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', ':instance', ':limit_to_local_content'])
return new Set(desc.suggestions.map(option => ({
label: option !== 'false' ? this.$t('admin_dash.instance.' + option) : this.$t('general.no'),
value: option
})))
}
} }
} }

View file

@ -1,17 +1,13 @@
<template> <template>
<div :label="$t('admin_dash.tabs.instance')"> <div :label="$t('admin_dash.tabs.instance')">
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('admin_dash.instance.instance') }}</h2> <h3>{{ $t('admin_dash.instance.instance') }}</h3>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<StringSetting path=":pleroma.:instance.:name" /> <StringSetting path=":pleroma.:instance.:name" />
</li> </li>
<!-- See https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3963 --> <li>
<li v-if="adminDraft[':pleroma'][':instance'][':favicon'] !== undefined"> <StringSetting path=":pleroma.:instance.:contact_username" />
<AttachmentSetting
compact
path=":pleroma.:instance.:favicon"
/>
</li> </li>
<li> <li>
<StringSetting path=":pleroma.:instance.:email" /> <StringSetting path=":pleroma.:instance.:email" />
@ -22,87 +18,63 @@
<li> <li>
<StringSetting path=":pleroma.:instance.:short_description" /> <StringSetting path=":pleroma.:instance.:short_description" />
</li> </li>
<li>
<ListSetting
force-new
ignore-suggestions
path=":pleroma.:instance.:languages"
/>
</li>
<li>
<StringSetting path=":pleroma.:instance.:status_page" />
</li>
</ul>
<h3>{{ $t('admin_dash.instance.branding') }}</h3>
<ul class="setting-list">
<!-- See https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3963 -->
<li v-if="adminDraft[':pleroma'][':instance'][':favicon'] !== undefined">
<AttachmentSetting
compact
path=":pleroma.:instance.:favicon"
/>
</li>
<li> <li>
<AttachmentSetting <AttachmentSetting
compact compact
path=":pleroma.:instance.:instance_thumbnail" path=":pleroma.:instance.:instance_thumbnail"
/> />
</li> </li>
<li>
<PWAManifestIconsSetting path=":pleroma.:manifest.:icons" />
</li>
<li>
<ColorSetting path=":pleroma.:manifest.:theme_color" />
</li>
<li>
<ColorSetting path=":pleroma.:manifest.:background_color" />
</li>
<li> <li>
<AttachmentSetting path=":pleroma.:instance.:background_image" /> <AttachmentSetting path=":pleroma.:instance.:background_image" />
</li> </li>
</ul> </ul>
</div> <h3>{{ $t('admin_dash.instance.rich_metadata') }}</h3>
<div class="setting-item">
<h2>{{ $t('admin_dash.instance.registrations') }}</h2>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<BooleanSetting path=":pleroma.:instance.:registrations_open" /> <ListSetting
<ul class="setting-list suboptions"> override-available-options
<li> :options="providersOptions"
<BooleanSetting :path="[':pleroma','Pleroma.Web.Metadata', ':providers']"
path=":pleroma.:instance.:invites_enabled" />
parent-path=":pleroma.:instance.:registrations_open"
parent-invert
/>
</li>
</ul>
</li> </li>
<li> <li>
<BooleanSetting path=":pleroma.:instance.:birthday_required" /> <BooleanSetting
<ul class="setting-list suboptions"> :path="[':pleroma','Pleroma.Web.Metadata', ':unfurl_nsfw']"
<li> />
<IntegerSetting
path=":pleroma.:instance.:birthday_min_age"
parent-path=":pleroma.:instance.:birthday_required"
/>
</li>
</ul>
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_activation_required" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_approval_required" />
</li>
<li>
<h3>{{ $t('admin_dash.instance.captcha_header') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Captcha', ':enabled']" />
<ul class="setting-list suboptions">
<li>
<ChoiceSetting
:path="[':pleroma', 'Pleroma.Captcha', ':method']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
:option-label-map="{
'Pleroma.Captcha.Native': $t('admin_dash.captcha.native'),
'Pleroma.Captcha.Kocaptcha': $t('admin_dash.captcha.kocaptcha')
}"
/>
<IntegerSetting
:path="[':pleroma', 'Pleroma.Captcha', ':seconds_valid']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
/>
</li>
<li
v-if="adminDraft[':pleroma']['Pleroma.Captcha'][':enabled'] && adminDraft[':pleroma']['Pleroma.Captcha'][':method'] === 'Pleroma.Captcha.Kocaptcha'"
>
<h4>{{ $t('admin_dash.instance.kocaptcha') }}</h4>
<ul class="setting-list">
<li>
<StringSetting :path="[':pleroma', 'Pleroma.Captcha.Kocaptcha', ':endpoint']" />
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li> </li>
</ul> </ul>
</div> </div>
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('admin_dash.instance.access') }}</h2> <h3>{{ $t('admin_dash.instance.access') }}</h3>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<BooleanSetting <BooleanSetting
@ -115,6 +87,8 @@
<ChoiceSetting <ChoiceSetting
override-backend-description override-backend-description
override-backend-description-label override-backend-description-label
override-available-options
:options="limitLocalContentOptions"
path=":pleroma.:instance.:limit_to_local_content" path=":pleroma.:instance.:limit_to_local_content"
/> />
</li> </li>

View file

@ -0,0 +1,34 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const JobQueuesTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
TupleSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
...SharedComputedObject()
}
}
export default JobQueuesTab

View file

@ -0,0 +1,199 @@
<template>
<div :label="$t('admin_dash.tabs.job_queues')">
<div class="setting-item">
<h3>{{ $t('admin_dash.job_queues.Oban') }}</h3>
<ul class="setting-list">
<li>
<ChoiceSetting path=":pleroma.Oban.:log" />
<h4>{{ $t('admin_dash.job_queues.queues') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:federator_incoming" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:federator_outgoing" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:attachments_cleanup" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:mailer" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:activity_expiration" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:scheduled_activities" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:transmogrifier" />
</li>
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:web_push" />
</li>
<!-- CONFIRM what is this queue -->
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:slow" />
</li>
<!-- CONFIRM what is this queue -->
<li>
<IntegerSetting path=":pleroma.Oban.:queues.:background" />
</li>
</ul>
<GroupSetting path=":pleroma.Oban.:queues" />
</li>
</ul>
<h3>{{ $t('admin_dash.job_queues.Gun.title') }}</h3>
<ul class="setting-list">
<li>
<h4>{{ $t('admin_dash.job_queues.Gun.connections_pools') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:connections_pool.:connect_timeout" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:connection_acquisition_retries" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:connection_acquisition_wait" />
</li>
<li>
<!-- CONFIRM what is this -->
<IntegerSetting path=":pleroma.:connections_pool.:retry" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:max_connections" />
</li>
<li>
<!-- CONFIRM what is this -->
<IntegerSetting path=":pleroma.:connections_pool.:max_idle_time" />
</li>
<li>
<IntegerSetting path=":pleroma.:connections_pool.:reclaim_multiplier" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.job_queues.Gun.pools.title') }}</h4>
<ul class="setting-list">
<li>
<h5>{{ $t('admin_dash.job_queues.Gun.pools.default') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:default.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:default.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:default.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:default" />
</li>
<li>
<h5>{{ $t('admin_dash.job_queues.Gun.pools.federation') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:federation.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:federation.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:federation.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:federation" />
</li>
<li>
<!-- CONFIRM what is this? -->
<h5>{{ $t('admin_dash.job_queues.Gun.pools.rich_media') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:rich_media.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:rich_media.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:rich_media.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:rich_media" />
</li>
<li>
<h5>{{ $t('admin_dash.job_queues.Gun.pools.media') }}</h5>
<ul class="setting-list suboptions">
<li>
<IntegerSetting path=":pleroma.:pools.:media.:size" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:media.:max_waiting" />
</li>
<li>
<IntegerSetting path=":pleroma.:pools.:media.:recv_timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:pools.:media" />
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.job_queues.Hackney.title') }}</h3>
<ul class="setting-list">
<li>
<h4>{{ $t('admin_dash.job_queues.Hackney.federation') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:federation.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:federation.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:federation" />
</li>
<li>
<h4>{{ $t('admin_dash.job_queues.Hackney.media') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:media.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:media.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:media" />
</li>
<li>
<!-- CONFIRM what is this -->
<h4>{{ $t('admin_dash.job_queues.Hackney.rich_media') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:rich_media.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:rich_media.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:rich_media" />
</li>
<li>
<h4>{{ $t('admin_dash.job_queues.Hackney.upload') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:upload.:max_connections" />
</li>
<li>
<IntegerSetting path=":pleroma.:hackney_pools.:upload.:timeout" />
</li>
</ul>
<GroupSetting path=":pleroma.:hackney_pools.:upload" />
</li>
</ul>
</div>
</div>
</template>
<script src="./job_queues_tab.js"></script>

View file

@ -4,14 +4,6 @@ import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue' import StringSetting from '../helpers/string_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js' import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faGlobe
)
const LimitsTab = { const LimitsTab = {
components: { components: {

View file

@ -1,10 +1,10 @@
<template> <template>
<div :label="$t('admin_dash.tabs.limits')"> <div :label="$t('admin_dash.tabs.limits')">
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('admin_dash.limits.arbitrary_limits') }}</h2> <h3>{{ $t('admin_dash.limits.arbitrary_limits') }}</h3>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<h3>{{ $t('admin_dash.limits.posts') }}</h3> <h4>{{ $t('admin_dash.limits.posts') }}</h4>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<IntegerSetting <IntegerSetting
@ -24,7 +24,7 @@
</ul> </ul>
</li> </li>
<li> <li>
<h3>{{ $t('admin_dash.limits.uploads') }}</h3> <h4>{{ $t('admin_dash.limits.uploads') }}</h4>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<IntegerSetting <IntegerSetting
@ -50,7 +50,7 @@
</ul> </ul>
</li> </li>
<li> <li>
<h3>{{ $t('admin_dash.limits.users') }}</h3> <h4>{{ $t('admin_dash.limits.users') }}</h4>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<IntegerSetting <IntegerSetting
@ -74,7 +74,7 @@
/> />
</li> </li>
<li> <li>
<h4>{{ $t('admin_dash.limits.profile_fields') }}</h4> <h5>{{ $t('admin_dash.limits.profile_fields') }}</h5>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<IntegerSetting <IntegerSetting
@ -108,7 +108,7 @@
</ul> </ul>
</li> </li>
<li> <li>
<h4>{{ $t('admin_dash.limits.user_uploads') }}</h4> <h5>{{ $t('admin_dash.limits.user_uploads') }}</h5>
<ul class="setting-list"> <ul class="setting-list">
<li> <li>
<IntegerSetting <IntegerSetting
@ -128,6 +128,25 @@
</li> </li>
</ul> </ul>
</li> </li>
<li>
<h4>{{ $t('admin_dash.limits.other') }}</h4>
<ul class="setting-list">
<li>
<IntegerSetting
source="admin"
path=":pleroma.:instance.:max_report_comment_size"
draft-mode
/>
</li>
<li>
<IntegerSetting
source="admin"
path=":pleroma.:instance.:max_endorsed_users"
draft-mode
/>
</li>
</ul>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View file

@ -0,0 +1,109 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { get } from 'lodash'
const LinksTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting,
ListSetting,
Checkbox
},
computed: {
classIsPresent () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':class'] !== false
},
relIsPresent () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':rel'] !== false
},
truncateIsPresent () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':truncate'] !== false
},
truncateDescription () {
return get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Formatter', ':truncate'])
},
ttlSettersOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:ttl_setters')
return new Set(desc.suggestions.map(option => ({
label: option.replace('Pleroma.Web.RichMedia.Parser.TTL.', ''),
value: option
})))
},
parsersOptions () {
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:parsers')
return new Set(desc.suggestions.map(option => ({
label: option.replace('Pleroma.Web.RichMedia.Parsers.', ''),
value: option
})))
},
validateTLDOptions () {
return [{
label: this.$t('general.yes'),
value: true
}, {
label: this.$t('general.no'),
value: false
}, {
label: this.$t('admin_dash.links.no_scheme'),
value: ':no_scheme'
}]
},
mediaProxyEnabled () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled']
},
mediaInvalidationProvider () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider']
},
...SharedComputedObject()
},
methods: {
checkRel (e) {
this.$store.commit(
'updateAdminDraft',
{
path: [':pleroma','Pleroma.Formatter',':rel'],
value: e ? '' : false
}
)
},
checkClass (e) {
this.$store.commit(
'updateAdminDraft',
{
path: [':pleroma','Pleroma.Formatter',':class'],
value: e ? '' : false
}
)
},
checkTruncate (e) {
this.$store.commit(
'updateAdminDraft',
{
path: [':pleroma','Pleroma.Formatter',':truncate'],
value: e ? 20 : false
}
)
}
}
}
export default LinksTab

View file

@ -0,0 +1,26 @@
.LinksTab {
// awkward "disable = false" backend options
.weird-options {
.checkbox {
margin: 0;
}
}
.setting-list.suboptions.weird-suboptions {
display: flex;
flex-direction: column;
gap: 0.5em;
margin: 0;
li {
margin: 0;
display: flex;
}
.GroupSetting {
display: inline-block;
margin: 0;
padding: 0;
}
}
}

View file

@ -0,0 +1,146 @@
<template>
<div
class="LinksTab"
:label="$t('admin_dash.tabs.media_proxy')"
>
<div class="setting-item">
<h3>{{ $t('admin_dash.links.link_previews') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:rich_media.:enabled" />
</li>
<li>
<ListSetting
override-available-options
:options="parsersOptions"
path=":pleroma.:rich_media.:parsers"
/>
</li>
<li>
<IntegerSetting
path=":pleroma.:rich_media.:timeout"
/>
</li>
<li>
<ListSetting
override-available-options
:options="ttlSettersOptions"
path=":pleroma.:rich_media.:ttl_setters"
/>
</li>
<li>
<ListSetting
path=":pleroma.:rich_media.:ignore_tld"
ignore-suggestions
/>
</li>
<li>
<ListSetting path=":pleroma.:rich_media.:ignore_hosts" />
</li>
</ul>
<h3>{{ $t('admin_dash.links.link_formatter') }}</h3>
<ul class="setting-list weird-options">
<li>
<Checkbox
:model-value="classIsPresent"
@update:model-value="checkClass"
>
<i18n-t
keypath="admin_dash.temp_overrides.:pleroma.Pleroma_DOT_Formatter.:attribute_toggle.label"
tag="span"
scope="global"
>
<template #attr>
<code>class</code>
</template>
</i18n-t>
</Checkbox>
<div class="setting-list suboptions weird-suboptions">
<StringSetting
v-if="classIsPresent"
:path="[':pleroma', 'Pleroma.Formatter', ':class']"
hide-label
hide-draft-buttons
/>
<GroupSetting :path="[':pleroma', 'Pleroma.Formatter', ':class']" />
</div>
</li>
<li>
<Checkbox
:model-value="relIsPresent"
@update:model-value="checkRel"
>
<i18n-t
keypath="admin_dash.temp_overrides.:pleroma.Pleroma_DOT_Formatter.:attribute_toggle.label"
tag="span"
scope="global"
>
<template #attr>
<code>rel</code>
</template>
</i18n-t>
</Checkbox>
<div class="setting-list suboptions weird-suboptions">
<StringSetting
v-if="relIsPresent"
:path="[':pleroma', 'Pleroma.Formatter', ':rel']"
hide-label
hide-draft-buttons
/>
<GroupSetting
:path="[':pleroma', 'Pleroma.Formatter', ':rel']"
/>
</div>
</li>
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Formatter', ':new_window']" />
</li>
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Formatter', ':strip_prefix']" />
</li>
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Formatter', ':extra']" />
</li>
<li>
<ChoiceSetting
:path="[':pleroma', 'Pleroma.Formatter', ':validate_tld']"
:options="validateTLDOptions"
override-options
/>
</li>
<li>
<Checkbox
:model-value="truncateIsPresent"
@update:model-value="checkTruncate"
>
{{ truncateDescription.label }}
</Checkbox>
<div class="setting-list suboptions weird-suboptions">
<li>
<IntegerSetting
v-if="truncateIsPresent"
:path="[':pleroma', 'Pleroma.Formatter', ':truncate']"
hide-label
hide-draft-buttons
/>
</li>
<li>
<GroupSetting :path="[':pleroma', 'Pleroma.Formatter', ':truncate']" />
</li>
</div>
</li>
<li>
<!-- CONFIRM backend what's difference between here and :extra -->
<ListSetting
ignore-suggestions
path=":pleroma.:uri_schemes.:valid_schemes"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./links_tab.js"></script>
<style lang="scss" src="./links_tab.scss"></style>

View file

@ -0,0 +1,63 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const MailerTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting
},
computed: {
adaptersLabels () {
const prefix = 'Swoosh.Adapters.'
const descriptions = this.$store.state.adminSettings.descriptions
const options = descriptions[':pleroma']['Pleroma.Emails.Mailer'][':adapter'].suggestions
return Object.fromEntries(options.map(value => [
value, value.replace(prefix, '')
]))
},
startTLSLabels () {
return {
':always': this.$t('admin_dash.generic_enforcement.always'),
':if_available': this.$t('admin_dash.generic_enforcement.if_available'),
':never': this.$t('admin_dash.generic_enforcement.never')
}
// return Object.fromEntries(options.map(value => [
// value, value.replace(prefix, '')
// ]))
},
adapter () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':adapter']
},
mailerEnabled () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':enabled']
},
...SharedComputedObject()
},
methods: {
adapterHasKey (key) {
const descriptions = this.$store.state.adminSettings.descriptions
const mailerStuff = descriptions[':pleroma']['Pleroma.Emails.Mailer']
const adapterStuff = mailerStuff[':subgroup,' + this.adapter]
return Object.prototype.hasOwnProperty.call(adapterStuff, key)
}
}
}
export default MailerTab

View file

@ -0,0 +1,150 @@
<template>
<div :label="$t('admin_dash.tabs.mailer')">
<div class="setting-item">
<h3>{{ $t('admin_dash.mailer.adapter') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Emails.Mailer',':enabled']" />
</li>
<template v-if="mailerEnabled">
<li>
<ChoiceSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':adapter']"
:option-label-map="adaptersLabels"
/>
<h4>{{ $t('admin_dash.mailer.auth') }}</h4>
<ul class="setting-list suboptions">
<li v-if="adapterHasKey(':api_key')">
<!-- authentication info -->
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':api_key']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':access_key')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':access_key']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':access_token')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':access_token']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':username')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':username']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':password')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':password']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':secret')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':secret']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':auth')">
<ChoiceSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':auth']"
:password="true"
:subgroup="adapter"
/>
</li>
<!-- server info -->
<li v-if="adapterHasKey(':relay')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':relay']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':ssl')">
<BooleanSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':ssl']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':tls')">
<ChoiceSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':tls']"
:option-label-map="startTLSLabels"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':port')">
<IntegerSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':port']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':server_id')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':server_id']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':region')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':region']"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':domain')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':domain']"
:subgroup="adapter"
/>
</li>
<!-- sendmail exclusive -->
<li v-if="adapterHasKey(':cmd_path')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':cmd_path']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':cmd_args')">
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':cmd_args']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':qmail')">
<BooleanSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':qmail']"
:password="true"
:subgroup="adapter"
/>
</li>
<li v-if="adapterHasKey(':retries')">
<IntegerSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':retries']"
:subgroup="adapter"
/>
</li>
</ul>
</li>
</template>
</ul>
</div>
</div>
</template>
<script src="./mailer_tab.js"></script>

View file

@ -0,0 +1,38 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const MediaProxyTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
mediaProxyEnabled () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled']
},
mediaInvalidationProvider () {
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider']
},
...SharedComputedObject()
}
}
export default MediaProxyTab

View file

@ -0,0 +1,135 @@
<template>
<div :label="$t('admin_dash.tabs.media_proxy')">
<div class="setting-item">
<h3>{{ $t('admin_dash.media_proxy.basic') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:media_proxy.:enabled" />
<ul
v-if="mediaProxyEnabled"
class="setting-list suboptions"
>
<li>
<StringSetting path=":pleroma.:media_proxy.:base_url" />
</li>
<li>
<BooleanSetting path=":pleroma.:media_proxy.:proxy_opts.:redirect_on_failure" />
</li>
<li>
<ListSetting
ignore-suggestions
path=":pleroma.:media_proxy.:whitelist"
/>
</li>
</ul>
</li>
</ul>
<template v-if="mediaProxyEnabled">
<h3>{{ $t('admin_dash.media_proxy.invalidation') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:media_proxy.:invalidation.:enabled" />
<ul class="setting-list suboptions">
<li>
<ChoiceSetting
path=":pleroma.:media_proxy.:invalidation.:provider"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<h4>{{ $t('admin_dash.media_proxy.invalidation_settings') }}</h4>
<ul class="setting-list suboptions">
<template v-if="mediaInvalidationProvider === 'Pleroma.Web.MediaProxy.Invalidation.Http'">
<li>
<StringSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Http', ':method']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<li>
<ListSetting
ignore-suggestions
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Http', ':headers']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<li>
<ListSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Http', ':options']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
</template>
<template v-if="mediaInvalidationProvider === 'Pleroma.Web.MediaProxy.Invalidation.Script'">
<!-- TODO: you know the drill by now - list component -->
<li>
<StringSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Script', ':script_path']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
<li>
<StringSetting
:path="[':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Script', ':url_format']"
parent-path=":pleroma.:media_proxy.:invalidation.:enabled"
/>
</li>
</template>
</ul>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.media_proxy.limits') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting
path=":pleroma.:media_proxy.:proxy_opts.:max_body_length"
/>
</li>
<li>
<IntegerSetting
path=":pleroma.:media_proxy.:proxy_opts.:max_read_duration"
/>
</li>
<li>
<GroupSetting path=":pleroma.:media_proxy.:proxy_opts" />
</li>
</ul>
<!-- TODO: add whitelist when we have list component (hehe) -->
<h3>{{ $t('admin_dash.media_proxy.thumbnails') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:media_preview_proxy.:enabled" />
<ul class="setting-list suboptions">
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:image_quality"
/>
</li>
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:min_content_length"
/>
</li>
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:thumbnail_max_width"
/>
</li>
<li>
<IntegerSetting
parent-path=":pleroma.:media_preview_proxy.:enabled"
path=":pleroma.:media_preview_proxy.:thumbnail_max_height"
/>
</li>
</ul>
</li>
</ul>
</template>
</div>
</div>
</template>
<script src="./media_proxy_tab.js"></script>

View file

@ -0,0 +1,42 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faGlobe
)
const MonitoringTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
...SharedComputedObject()
},
methods: {
}
}
export default MonitoringTab

View file

@ -0,0 +1,47 @@
<template>
<div :label="$t('admin_dash.tabs.monitoring')">
<div class="setting-item">
<h3>{{ $t('admin_dash.monitoring.builtins') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Emails.NewUsersDigestEmail',':enabled']" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:healthcheck" />
</li>
</ul>
<h3>{{ $t('admin_dash.monitoring.prometheus') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']" />
</li>
<li>
<BooleanSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':auth']"
/>
</li>
<li>
<StringSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':path']"
/>
</li>
<li>
<ChoiceSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':format']"
/>
</li>
<li>
<ListSetting
:parent-path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':enabled']"
:path="[':prometheus','Pleroma.Web.Endpoint.MetricsExporter',':ip_whitelist']"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./monitoring_tab.js"></script>

View file

@ -0,0 +1,38 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import ColorSetting from '../helpers/color_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import PWAManifestIconsSetting from '../helpers/pwa_manifest_icons_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const OtherTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
ColorSetting,
AttachmentSetting,
ListSetting,
PWAManifestIconsSetting,
MapSetting,
GroupSetting
},
computed: {
...SharedComputedObject()
}
}
export default OtherTab

View file

@ -0,0 +1,134 @@
<template>
<div :label="$t('admin_dash.tabs.other')">
<div class="setting-item">
<h3>{{ $t('admin_dash.other.uncategorized') }}</h3>
<ul class="setting-list">
<li>
<StringSetting path=":pleroma.:instance.:static_dir" />
</li>
<li>
<StringSetting path=":pleroma.:modules.:runtime_dir" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:profile_directory" />
</li>
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Web.ApiSpec.CastAndValidate',':strict']" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.reports') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:report_strip_status" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.user_backup') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':purge_after_days']" />
</li>
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':limit_days']" />
</li>
<!-- CONFIRM what is this?
<li>
<StringSetting :path="[':pleroma','Pleroma.User.Backup',':dir']" />
</li>
-->
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':process_chunk_size']" />
</li>
<li>
<IntegerSetting :path="[':pleroma','Pleroma.User.Backup',':timeout']" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.feed') }}</h3>
<ul class="setting-list">
<h4>{{ $t('admin_dash.other.feed_title') }}</h4>
<li>
<IntegerSetting path=":pleroma.:feed.:post_title.:max_length" />
</li>
<li>
<StringSetting path=":pleroma.:feed.:post_title.:omission" />
</li>
<li>
<GroupSetting path=":pleroma.:feed.:post_title" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.remote_ip') }}</h3>
<p>{{ $t('admin_dash.other.remote_ip_description') }}</p>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':enabled']" />
</li>
<li>
<ListSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':headers']" />
</li>
<li>
<ListSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':proxies']" />
</li>
<li>
<ListSetting :path="[':pleroma','Pleroma.Web.Plugs.RemoteIp',':reserved']" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.mime') }}</h3>
<ul class="setting-list">
<li>
<ListSetting
override-backend-description-label="application/activity+json"
hide-description
:path="[':mime',':types', 'application/activity+json']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/jrd+json"
hide-description
:path="[':mime',':types', 'application/jrd+json']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/ld+json"
hide-description
:path="[':mime',':types', 'application/ld+json']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/xml"
hide-description
:path="[':mime',':types', 'application/xml']"
/>
</li>
<li>
<ListSetting
override-backend-description-label="application/xrd+xml"
hide-description
:path="[':mime',':types', 'application/xrd+xml']"
/>
</li>
</ul>
<h3>{{ $t('admin_dash.other.streamer') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:streamer.:workers" />
</li>
<li>
<IntegerSetting path=":pleroma.:streamer.:overflow_workers" />
</li>
</ul>
<h3>{{ $t('admin_dash.other.privileges') }}</h3>
<ul class="setting-list">
<li>
<ListSetting path=":pleroma.:instance.:admin_privileges" />
</li>
<li>
<ListSetting path=":pleroma.:instance.:moderator_privileges" />
</li>
</ul>
</div>
</div>
</template>
<script src="./other_tab.js"></script>

View file

@ -0,0 +1,38 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import ColorSetting from '../helpers/color_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import PWAManifestIconsSetting from '../helpers/pwa_manifest_icons_setting.vue'
import MapSetting from '../helpers/map_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const PostsTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
ColorSetting,
AttachmentSetting,
ListSetting,
PWAManifestIconsSetting,
MapSetting,
GroupSetting
},
computed: {
...SharedComputedObject()
}
}
export default PostsTab

View file

@ -0,0 +1,43 @@
<template>
<div :label="$t('admin_dash.tabs.posts')">
<div class="setting-item">
<h3>{{ $t('admin_dash.posts.global') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instances_favicons.:enabled" />
</li>
</ul>
<h3>{{ $t('admin_dash.posts.local') }}</h3>
<ul class="setting-list">
<li>
<ListSetting path=":pleroma.:instance.:allowed_post_formats" />
</li>
<li>
<h4>{{ $t('admin_dash.posts.scheduled_activites') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.ScheduledActivity', ':enabled']" />
</li>
<li>
<IntegerSetting :path="[':pleroma', 'Pleroma.ScheduledActivity', ':daily_user_limit']" />
</li>
<li>
<IntegerSetting :path="[':pleroma', 'Pleroma.ScheduledActivity', ':total_user_limit']" />
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.posts.remote') }}</h3>
<ul class="setting-list">
<li>
<IntegerSetting path=":pleroma.:instance.:remote_post_retention_days" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:skip_thread_containment" />
</li>
</ul>
</div>
</div>
</template>
<script src="./posts_tab.js"></script>

View file

@ -0,0 +1,34 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import TupleSetting from '../helpers/tuple_setting.vue'
import GroupSetting from '../helpers/group_setting.vue'
import AttachmentSetting from '../helpers/attachment_setting.vue'
import ListSetting from '../helpers/list_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const RegistrationsTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting,
TupleSetting,
AttachmentSetting,
GroupSetting,
ListSetting
},
computed: {
...SharedComputedObject()
}
}
export default RegistrationsTab

View file

@ -0,0 +1,188 @@
<template>
<div :label="$t('admin_dash.tabs.instance')">
<div class="setting-item">
<h3>{{ $t('admin_dash.instance.registrations') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:registrations_open" />
<ul class="setting-list suboptions">
<li>
<BooleanSetting
path=":pleroma.:instance.:invites_enabled"
parent-path=":pleroma.:instance.:registrations_open"
parent-invert
/>
</li>
</ul>
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:birthday_required" />
<ul class="setting-list suboptions">
<li>
<IntegerSetting
path=":pleroma.:instance.:birthday_min_age"
parent-path=":pleroma.:instance.:birthday_required"
/>
</li>
</ul>
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_activation_required" />
</li>
<li>
<BooleanSetting path=":pleroma.:instance.:account_approval_required" />
</li>
<li>
<h4>{{ $t('admin_dash.instance.captcha_header') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting :path="[':pleroma', 'Pleroma.Captcha', ':enabled']" />
<ul class="setting-list suboptions">
<li>
<ChoiceSetting
:path="[':pleroma', 'Pleroma.Captcha', ':method']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
:option-label-map="{
'Pleroma.Captcha.Native': $t('admin_dash.captcha.native'),
'Pleroma.Captcha.Kocaptcha': $t('admin_dash.captcha.kocaptcha')
}"
/>
<IntegerSetting
:path="[':pleroma', 'Pleroma.Captcha', ':seconds_valid']"
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
/>
</li>
<li
v-if="adminDraft[':pleroma']['Pleroma.Captcha'][':enabled'] && adminDraft[':pleroma']['Pleroma.Captcha'][':method'] === 'Pleroma.Captcha.Kocaptcha'"
>
<h5>{{ $t('admin_dash.instance.kocaptcha') }}</h5>
<ul class="setting-list">
<li>
<StringSetting :path="[':pleroma', 'Pleroma.Captcha.Kocaptcha', ':endpoint']" />
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.registrations.autofollow') }}</h3>
<ul class="setting-list">
<li>
<ListSetting
path=":pleroma.:instance.:autofollowed_nicknames"
/>
</li>
<li>
<ListSetting
path=":pleroma.:instance.:autofollowing_nicknames"
/>
</li>
</ul>
<h3>{{ $t('admin_dash.registrations.welcome.title') }}</h3>
<ul class="setting-list">
<p>{{ $t('admin_dash.registrations.welcome.description') }}</p>
<li>
<h4>{{ $t('admin_dash.registrations.welcome.direct_message') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:welcome.:direct_message.:enabled"
/>
<ul class="setting-list suboptions">
<li>
<StringSetting
path=":pleroma.:welcome.:direct_message.:sender_nickname"
parent-path=":pleroma.:welcome.:direct_message.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:direct_message.:message"
parent-path=":pleroma.:welcome.:direct_message.:enabled"
/>
</li>
</ul>
<GroupSetting path=":pleroma.:welcome.:direct_message" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.registrations.welcome.chat_message') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:welcome.:chat_message.:enabled"
/>
<ul class="setting-list suboptions">
<li>
<StringSetting
tuple
path=":pleroma.:welcome.:chat_message.:sender_nickname"
parent-path=":pleroma.:welcome.:chat_message.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:chat_message.:message"
parent-path=":pleroma.:welcome.:chat_message.:enabled"
/>
</li>
</ul>
<GroupSetting path=":pleroma.:welcome.:chat_message" />
</li>
</ul>
</li>
<li>
<h4>{{ $t('admin_dash.registrations.welcome.email_message') }}</h4>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:welcome.:email.:enabled"
/>
<ul class="setting-list suboptions">
<li>
<TupleSetting
path=":pleroma.:welcome.:email.:sender"
parent-path=":pleroma.:welcome.:email.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:email.:subject"
parent-path=":pleroma.:welcome.:email.:enabled"
/>
</li>
<li>
<StringSetting
path=":pleroma.:welcome.:email.:html"
parent-path=":pleroma.:welcome.:email.:enabled"
/>
</li>
</ul>
<GroupSetting path=":pleroma.:welcome.:email" />
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.registrations.restrictions') }}</h3>
<ul class="setting-list">
<li>
<ListSetting
ignore-suggestions
:path="[':pleroma', 'Pleroma.User', ':restricted_nicknames']"
/>
</li>
<li>
<ListSetting
ignore-suggestions
:path="[':pleroma', 'Pleroma.User', ':email_blacklist']"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./registrations_tab.js"></script>

View file

@ -0,0 +1,46 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
import StringSetting from '../helpers/string_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
const UploadsTab = {
provide () {
return {
defaultDraftMode: true,
defaultSource: 'admin'
}
},
data () {
return {
uploaders: [{
key: 'Pleroma.Uploaders.Local',
value: 'Pleroma.Uploaders.Local',
label: this.$t('admin_dash.uploads.local_uploader')
}, {
key: 'Pleroma.Uploaders.IPFS',
value: 'Pleroma.Uploaders.IPFS',
label: 'IPFS'
}, {
key: 'Pleroma.Uploaders.S3',
value: 'Pleroma.Uploaders.S3',
label: 'S3'
}]
}
},
components: {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
StringSetting
},
computed: {
uploader () {
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Upload'][':uploader']
},
...SharedComputedObject()
}
}
export default UploadsTab

View file

@ -0,0 +1,110 @@
<template>
<div :label="$t('admin_dash.tabs.uploads')">
<div class="setting-item">
<h3>{{ $t('admin_dash.uploads.upload') }}</h3>
<ul class="setting-list">
<li>
<ChoiceSetting
:path="[':pleroma','Pleroma.Upload',':uploader']"
:options="uploaders"
/>
<h4>{{ $t('admin_dash.uploads.uploader_settings') }}</h4>
<ul class="setting-list suboptions">
<template v-if="uploader === 'Pleroma.Uploaders.Local'">
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.Local',':uploads']"
/>
</li>
</template>
<template v-else-if="uploader === 'Pleroma.Uploaders.IPFS'">
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.IPFS',':get_gateway_url']"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.IPFS',':post_gateway_url']"
/>
</li>
</template>
<template v-else-if="uploader === 'Pleroma.Uploaders.S3'">
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':bucket']"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':bucket_namespace']"
/>
</li>
<li>
<BooleanSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':streaming_enabled']"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Uploaders.S3',':truncated_namespace']"
/>
</li>
</template>
<li>
<IntegerSetting
:path="[':pleroma','Pleroma.Uploaders.Uploader',':timeout']"
/>
</li>
</ul>
</li>
</ul>
<h3>{{ $t('admin_dash.uploads.attachments') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting
path=":pleroma.:instance.:attachment_links"
:options="uploaders"
/>
</li>
<li>
<BooleanSetting
path=":pleroma.:instance.:cleanup_attachments"
:options="uploaders"
/>
</li>
</ul>
<!-- CONFIRM how filters work -->
<h3>{{ $t('admin_dash.uploads.filenames') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting
:path="[':pleroma','Pleroma.Upload',':link_name']"
:subgroup="adapter"
/>
</li>
<li>
<IntegerSetting
:path="[':pleroma','Pleroma.Upload',':filename_display_max_length']"
:subgroup="adapter"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Upload',':default_description']"
:subgroup="adapter"
/>
</li>
<li>
<StringSetting
:path="[':pleroma','Pleroma.Upload',':base_url']"
:subgroup="adapter"
/>
</li>
<!-- TODO: add mime-type when we have a dynamic list component -->
</ul>
</div>
</div>
</template>
<script src="./uploads_tab.js"></script>

View file

@ -22,6 +22,7 @@
<slot v-else /> <slot v-else />
</span> </span>
</Checkbox> </Checkbox>
{{ ' ' }}
<ModifiedIndicator <ModifiedIndicator
:changed="isChanged" :changed="isChanged"
:onclick="reset" :onclick="reset"

View file

@ -9,6 +9,10 @@ export default {
}, },
props: { props: {
...Setting.props, ...Setting.props,
overrideOptions: {
type: Boolean,
required: false
},
options: { options: {
type: Array, type: Array,
required: false required: false
@ -22,7 +26,16 @@ export default {
computed: { computed: {
...Setting.computed, ...Setting.computed,
realOptions () { realOptions () {
if (this.overrideOptions) {
return this.options
}
if (this.realSource === 'admin') { if (this.realSource === 'admin') {
if (
!this.backendDescriptionSuggestions?.length ||
this.backendDescriptionSuggestions?.length === 0
) {
return this.options
}
return this.backendDescriptionSuggestions.map(x => ({ return this.backendDescriptionSuggestions.map(x => ({
key: x, key: x,
value: x, value: x,

View file

@ -2,6 +2,7 @@
<label <label
v-if="matchesExpertLevel" v-if="matchesExpertLevel"
class="ChoiceSetting" class="ChoiceSetting"
:class="{ 'faint': shouldBeDisabled }"
> >
<template v-if="backendDescriptionLabel"> <template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel }} {{ backendDescriptionLabel }}
@ -11,8 +12,8 @@
</template> </template>
{{ ' ' }} {{ ' ' }}
<Select <Select
:model-value="realDraftMode ? draft :state" :model-value="realDraftMode ? draft : state"
:disabled="disabled" :disabled="shouldBeDisabled"
@update:model-value="update" @update:model-value="update"
> >
<option <option

View file

@ -0,0 +1,17 @@
import Setting from './setting.js'
import ColorInput from 'src/components/color_input/color_input.vue'
export default {
...Setting,
components: {
...Setting.components,
ColorInput
},
methods: {
...Setting.methods,
getValue (e) {
console.log(e)
return e
}
}
}

View file

@ -0,0 +1,54 @@
<template>
<label
v-if="matchesExpertLevel"
class="ColorSetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
{{ ' ' }}
<ColorInput
:id="path"
class="color-setting-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions"
:model-value="realDraftMode ? draft : state"
@update:model-value="update"
/>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
</label>
</template>
<script src="./color_setting.js"></script>
<style lang="scss">
.ColorSetting {
.color-setting-input {
vertical-align: middle;
}
}
</style>

View file

@ -2,6 +2,7 @@
<!-- TODO make it reusable --> <!-- TODO make it reusable -->
<template> <template>
<span <span
v-if="$parent.isDirty || $parent.canHardReset"
class="DraftButtons" class="DraftButtons"
> >
<Popover <Popover
@ -74,10 +75,9 @@ export default {
.DraftButtons { .DraftButtons {
display: inline-block; display: inline-block;
position: relative; position: relative;
display: inline-flex;
.button-default { gap: 0.5em;
margin-left: 0.5em; margin-top: 0.5em
}
} }
.draft-tooltip { .draft-tooltip {

View file

@ -1,13 +1,5 @@
import { isEqual } from 'lodash'
import Setting from './setting.js' import Setting from './setting.js'
export default { export default {
...Setting, ...Setting,
computed: {
...Setting.computed,
isDirty () {
return !isEqual(this.state, this.draft)
}
}
} }

View file

@ -0,0 +1,133 @@
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Setting from './setting.js'
export default {
...Setting,
data () {
return {
newValue: '',
}
},
components: {
...Setting.components,
Checkbox
},
props: {
...Setting.props,
ignoreSuggestions: {
required: false,
type: Boolean
},
overrideAvailableOptions: {
required: false,
type: Boolean
},
options: {
required: false,
type: Set
},
allowNew: {
required: false,
type: Boolean,
default: true
},
forceNew: {
required: false,
type: Boolean,
default: false
}
},
computed: {
...Setting.computed,
showNew () {
if (this.forceNew) return true
if (!this.allowNew) return false
const isExpert = this.$store.state.config.expertLevel > 0
const hasBuiltins = this.builtinEntries.length > 0
if (hasBuiltins) {
return isExpert
} else {
return true
}
},
valueSet () {
return new Set(this.visibleState)
},
suggestionsSet () {
const suggestions = this.backendDescriptionSuggestions
if (suggestions) {
return new Set(suggestions)
} else {
return new Set()
}
},
extraEntries () {
if (this.ignoreSuggestions) return [...this.valueSet.values()]
if (!this.suggestionsSet) return []
return [...this.valueSet.values()].filter((x) => {
return !this.builtinEntriesValueSet.has(x)
})
},
builtinEntries () {
if (this.ignoreSuggestions) return []
if (this.overrideAvailableOptions) {
return [...this.options]
}
if (!this.suggestionsSet) return []
const builtins = [...this.suggestionsSet.values()]
return builtins.map((option) => ({
label: option,
value: option
}))
},
builtinEntriesValueSet () {
return new Set(this.builtinEntries.map(x => x.value))
}
},
methods: {
...Setting.methods,
optionPresent (option) {
return this.valueSet.has(option)
},
getValue ({ event, value, index, eventType }) {
switch (eventType) {
case 'toggle': {
this.newValue = ''
const newSet = new Set(this.valueSet.values())
if (event) {
newSet.add(value)
} else {
newSet.delete(value)
}
return [...newSet.values()]
}
case 'add': {
if (!this.newValue) return this.valueSet.values()
const res = [...this.valueSet.values(), this.newValue]
this.newValue = ''
return res
}
case 'remove': {
const pre = [...this.valueSet.values()].slice(0, index)
const post = [...this.valueSet.values()].slice(index + 1)
return [...pre, ...post]
}
case 'edit': {
const pre = [...this.valueSet.values()].slice(0, index)
const post = [...this.valueSet.values()].slice(index + 1)
const string = event.target.value
if (!string) return [...this.valueSet.values()]
return [...pre, string, ...post]
}
}
}
}
}

View file

@ -0,0 +1,90 @@
<template>
<div
v-if="matchesExpertLevel"
class="ListSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, i) in builtinEntries"
:key="i"
>
<Checkbox
:disabled="shouldBeDisabled"
:model-value="optionPresent(item.value)"
@update:model-value="event => update({ event, value: item.value, eventType: 'toggle' })"
>
{{ item.label }}
</Checkbox>
</li>
<li
v-for="(item, index) in extraEntries"
:key="index"
>
<div class="btn-group">
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item"
@change="e => update({ event: e, index, eventType: 'edit' })"
>
<button
class="button-default"
@click="e => update({ index, eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</li>
<li v-if="showNew">
<div class="btn-group">
<input
v-model="newValue"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
>
<button
class="button-default"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./list_setting.js"></script>
<style lang="scss">
.ListSetting {
.btn-group {
display: flex
}
}
</style>

View file

@ -0,0 +1,44 @@
import ListSetting from './list_setting.js'
export default {
...ListSetting,
data () {
return {
newValue: ['','']
}
},
methods: {
...ListSetting.methods,
getValue ({ event, index, eventType, tuple }) {
switch (eventType) {
case 'add': {
if (!this.newValue[0] || !this.newValue[1]) return this.visibleState
const res = [...this.visibleState, this.newValue]
this.newValue = ['', '']
return res
}
case 'remove': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
return [...pre, ...post]
}
case 'edit': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
const item = this.visibleState[index]
const string = event.target.value
if (!string) return this.visibleState
if (tuple === 0) {
return [...pre, [string, item[1]], ...post]
} else {
return [...pre, [item[0], string], ...post]
}
}
}
}
}
}

View file

@ -0,0 +1,113 @@
<template>
<div
v-if="matchesExpertLevel"
class="ListTupleSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, index) in visibleState"
:key="index"
>
<div class="btn-group">
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item.tuple[0]"
@change="e => update({ event: e, index, eventType: 'edit', tuple: 0 })"
>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item.tuple[1]"
@change="e => update({ event: e, index, eventType: 'edit', tuple: 1 })"
>
<button
class="button-default"
@click="e => update({ index, eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</li>
<li>
<div class="btn-group">
<input
v-model="newValue[0]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][0]"
>
<input
v-model="newValue[1]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][1]"
>
<button
class="button-default"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./list_tuple_setting.js"></script>
<style lang="scss">
.ListTupleSetting {
.btn-group {
display: flex
}
dl {
display: inline-grid;
grid-template-columns: auto auto;
gap: 0.5em;
align-items: baseline;
dt {
display: inline;
font-weight: 800;
&::after {
content: ':'
}
}
dd {
display: inline;
margin: 0
}
}
}
</style>

View file

@ -0,0 +1,70 @@
import Setting from './setting.js'
export default {
...Setting,
props: {
...Setting.props,
allowNew: {
required: false,
type: Boolean,
default: true
}
},
data () {
return {
newValue: ['',''] // avoiding extra complexity by just using an array instead of an object
}
},
computed: {
...Setting.computed,
// state that we'll show in the UI, i.e. transforming map into list
displayState () {
return Object.entries(this.visibleState)
}
},
methods: {
...Setting.methods,
getValue ({ event, key, eventType, isKey }) {
switch (eventType) {
case 'add': {
if (key === '') return this.visibleState
const res = {...this.visibleState, ...Object.fromEntries([this.newValue])}
this.newValue = ['', '']
return res
}
case 'remove': {
// initial state for this type is empty array
if (Array.isArray(this.visibleState)) return this.visibleState
const newEntries = Object.entries(this.visibleState).filter(([k]) => k !== key)
if (newEntries.length === 0 ) return []
return Object.fromEntries(newEntries)
}
case 'edit': {
const string = event.target.value
const newEntries = Object
.entries(this.visibleState)
.map(([k, v]) => {
if (isKey) {
if (k === key) {
return [string, v]
} else {
return [k, v]
}
} else {
if (k === key) {
return [k, string]
} else {
return [k, v]
}
}
})
return Object.fromEntries(newEntries)
}
}
}
}
}

View file

@ -0,0 +1,112 @@
<template>
<div
v-if="matchesExpertLevel"
class="MapSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, i) in displayState"
:key="i"
>
<div class="btn-group">
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[0]"
@change="e => update({ event: e, key: item[0], eventType: 'edit', isKey: true })"
>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[1]"
@change="e => update({ event: e, key: item[0], eventType: 'edit', isKey: false })"
>
<button
class="button-default"
@click="e => update({ key: item[0], eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</li>
<li v-if="allowNew">
<div class="btn-group">
<input
v-model="newValue[0]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][0]"
>
<input
v-model="newValue[1]"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0][1]"
>
<button
class="button-default"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./map_setting.js"></script>
<style lang="scss">
.ListSetting {
.btn-group {
display: flex
}
dl {
display: inline-grid;
grid-template-columns: auto auto;
gap: 0.5em;
dt {
display: inline;
font-weight: 800;
&::after {
content: ':'
}
}
dd {
display: inline;
margin: 0
}
}
}
</style>

View file

@ -4,7 +4,9 @@
class="NumberSetting" class="NumberSetting"
> >
<label <label
v-if="!hideLabel"
:for="path" :for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }" :class="{ 'faint': shouldBeDisabled }"
> >
<template v-if="backendDescriptionLabel"> <template v-if="backendDescriptionLabel">
@ -22,6 +24,7 @@
type="number" type="number"
:step="step || 1" :step="step || 1"
:disabled="shouldBeDisabled" :disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions"
:min="min || 0" :min="min || 0"
:value="realDraftMode ? draft :state" :value="realDraftMode ? draft :state"
@change="update" @change="update"
@ -32,7 +35,7 @@
:onclick="reset" :onclick="reset"
/> />
<ProfileSettingIndicator :is-profile="isProfileSetting" /> <ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons /> <DraftButtons v-if="!hideDraftButtons" />
<p <p
v-if="backendDescriptionDescription" v-if="backendDescriptionDescription"
class="setting-description" class="setting-description"

View file

@ -0,0 +1,53 @@
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Setting from './setting.js'
const getUrl = state => state?.tuple ? state.tuple[1] + ':' + state.tuple[2] : state
const getSocks = state => state?.tuple
export default {
...Setting,
data () {
return {
urlField: '',
socksField: false
}
},
created () {
Setting.created()
this.urlField = getUrl(this.realDraftMode ? this.draft : this.state)
this.socksField = getSocks(this.realDraftMode ? this.draft : this.state)
},
computed: {
...Setting.computed,
// state that we'll show in the UI, i.e. transforming map into list
displayState () {
if (this.visibleState?.tuple) {
return this.visibleState.tuple[1] + ':' + this.visibleState.tuple[2]
}
return this.visibleState
},
socksState () {
return getSocks(this.visibleState)
}
},
components: {
...Setting.components,
Checkbox
},
methods: {
...Setting.methods,
getValue ({ event, isProxy}) {
if (isProxy) {
this.socksField = event
} else {
this.urlField = event.target.value
}
if (this.socksField) {
return { tuple: [ ':socks5', ...this.urlField.split(':') ] }
} else {
return this.urlField
}
}
}
}

View file

@ -0,0 +1,57 @@
<template>
<label
v-if="matchesExpertLevel"
class="ProxySetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions[0]"
:value="displayState"
@change="event => update({ event })"
>
{{ ' ' }}
<Checkbox
:model-value="socksState"
:disabled="shouldBeDisabled"
:indeterminate="isIndeterminate"
@update:model-value="event => update({ event, isProxy: true})"
>
{{ $t('admin_dash.http.socks5') }}
{{ ' ' }}
</Checkbox>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
</label>
</template>
<script src="./proxy_setting.js"></script>

View file

@ -0,0 +1,45 @@
import { clone } from 'lodash'
import Setting from './setting.js'
export default {
...Setting,
methods: {
...Setting.methods,
optionPresent (option) {
return this.valueSet.has(option)
},
getValue ({ event, field, index, eventType }) {
switch (eventType) {
case 'add': {
const res = [...this.visibleState, {}]
return res
}
case 'remove': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
return [...pre, ...post]
}
case 'edit': {
const pre = this.visibleState.slice(0, index)
const post = this.visibleState.slice(index + 1)
const item = clone(this.visibleState[index])
const string = event.target.value
console.log(item)
if (!string) {
delete item[field]
} else {
item[field] = string
}
console.log(item)
return [...pre, item, ...post]
}
}
}
}
}

View file

@ -0,0 +1,138 @@
<template>
<div
v-if="matchesExpertLevel"
class="PWAManifestIconsSetting"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, index) in visibleState"
:key="index"
>
<div>
<dl>
<dt><code>purpose</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':purpose']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':purpose' })"
>
</dd>
<dt><code>sizes</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':sizes']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':sizes' })"
>
</dd>
<dt><code>src</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':src']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':src' })"
>
</dd>
<dt><code>type</code></dt>
<dd>
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item[':type']"
@change="e => update({ event: e, index, eventType: 'edit', field: ':type' })"
>
</dd>
</dl>
<div class="buttons">
<button
v-if="index === visibleState.length - 1"
class="button-default add-button"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
<button
class="button-default delete-button"
@click="e => update({ index, eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./pwa_manifest_icons_setting.js"></script>
<style lang="scss">
.PWAManifestIconsSetting {
display: inline-block;
.setting-list {
display: grid;
gap: 0.5em;
}
.buttons {
display: flex;
gap: 0.5em;
justify-content: right;
margin-top: 0.5em;
button {
line-height: 2;
}
}
dl {
display: inline-grid;
grid-template-columns: auto auto;
gap: 0.5em;
align-items: baseline;
dt {
display: inline;
font-weight: 800;
text-align: right;
&::after {
content: ':'
}
}
dd {
display: inline;
margin: 0
}
}
}
</style>

View file

@ -1,7 +1,7 @@
import ModifiedIndicator from './modified_indicator.vue' import ModifiedIndicator from './modified_indicator.vue'
import ProfileSettingIndicator from './profile_setting_indicator.vue' import ProfileSettingIndicator from './profile_setting_indicator.vue'
import DraftButtons from './draft_buttons.vue' import DraftButtons from './draft_buttons.vue'
import { get, set, cloneDeep } from 'lodash' import { get, set, cloneDeep, isEqual } from 'lodash'
export default { export default {
components: { components: {
@ -18,6 +18,18 @@ export default {
type: [String, Array], type: [String, Array],
required: false required: false
}, },
descriptionPathOverride: {
type: [String, Array],
required: false
},
suggestions: {
type: [String, Array],
required: false
},
subgroup: {
type: String,
required: false
},
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false
@ -37,12 +49,22 @@ export default {
type: String, type: String,
default: undefined default: undefined
}, },
hideDraftButtons: { // this is for the weird backend hybrid (Boolean|String or Boolean|Number) settings
required: false,
type: Boolean
},
hideLabel: {
type: Boolean
},
hideDescription: { hideDescription: {
type: Boolean type: Boolean
}, },
swapDescriptionAndLabel: { swapDescriptionAndLabel: {
type: Boolean type: Boolean
}, },
backendDescriptionPath: {
type: [String, Array]
},
overrideBackendDescription: { overrideBackendDescription: {
type: Boolean type: Boolean
}, },
@ -73,7 +95,7 @@ export default {
}, },
created () { created () {
if (this.realDraftMode && (this.realSource !== 'admin' || this.path == null)) { if (this.realDraftMode && (this.realSource !== 'admin' || this.path == null)) {
this.draft = this.state this.draft = cloneDeep(this.state)
} }
}, },
computed: { computed: {
@ -114,10 +136,13 @@ export default {
return typeof this.draftMode === 'undefined' ? this.defaultDraftMode : this.draftMode return typeof this.draftMode === 'undefined' ? this.defaultDraftMode : this.draftMode
}, },
backendDescription () { backendDescription () {
return get(this.$store.state.adminSettings.descriptions, this.path) return get(this.$store.state.adminSettings.descriptions, this.descriptionPath)
}, },
backendDescriptionLabel () { backendDescriptionLabel () {
if (this.realSource !== 'admin') return '' if (this.realSource !== 'admin') return ''
if (this.overrideBackendDescriptionLabel !== '' && typeof this.overrideBackendDescriptionLabel === 'string') {
return this.overrideBackendDescriptionLabel
}
if (!this.backendDescription || this.overrideBackendDescriptionLabel) { if (!this.backendDescription || this.overrideBackendDescriptionLabel) {
return this.$t([ return this.$t([
'admin_dash', 'admin_dash',
@ -148,13 +173,20 @@ export default {
} }
}, },
backendDescriptionSuggestions () { backendDescriptionSuggestions () {
return this.backendDescription?.suggestions return this.backendDescription?.suggestions || this.suggestions
}, },
shouldBeDisabled () { shouldBeDisabled () {
if (this.path == null) { if (this.path == null) {
return this.disabled return this.disabled
} }
const parentValue = this.parentPath !== undefined ? get(this.configSource, this.parentPath) : null let parentValue = null
if (this.parentPath !== undefined && this.realSource === 'admin') {
if (this.realDraftMode) {
parentValue = get(this.$store.state.adminSettings.draft, this.parentPath)
} else {
parentValue = get(this.configSource, this.parentPath)
}
}
return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false) return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false)
}, },
configSource () { configSource () {
@ -209,17 +241,29 @@ export default {
if (this.path == null) return null if (this.path == null) return null
return Array.isArray(this.path) ? this.path : this.path.split('.') return Array.isArray(this.path) ? this.path : this.path.split('.')
}, },
descriptionPath () {
if (this.path == null) return null
if (this.descriptionPathOverride) return this.descriptionPathOverride
const path = Array.isArray(this.path) ? this.path : this.path.split('.')
if (this.subgroup) {
return [
...path.slice(0, path.length - 1),
':subgroup,' + this.subgroup,
...path.slice(path.length - 1)
]
}
return path
},
isDirty () { isDirty () {
if (this.path == null) return false if (this.path == null) return false
if (this.realSource === 'admin' && this.canonPath.length > 3) { if (this.realSource === 'admin' && this.canonPath.length > 3) {
return false // should not show draft buttons for "grouped" values return false // should not show draft buttons for "grouped" values
} else { } else {
return this.realDraftMode && this.draft !== this.state return this.realDraftMode && !isEqual(this.draft, this.state)
} }
}, },
canHardReset () { canHardReset () {
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths && return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths?.has(this.canonPath.join(' -> '))
this.$store.state.adminSettings.modifiedPaths.has(this.canonPath.join(' -> '))
}, },
matchesExpertLevel () { matchesExpertLevel () {
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0 return (this.expert || 0) <= this.$store.state.config.expertLevel > 0

View file

@ -4,6 +4,7 @@
class="StringSetting" class="StringSetting"
> >
<label <label
v-if="!hideLabel"
:for="path" :for="path"
class="setting-label" class="setting-label"
:class="{ 'faint': shouldBeDisabled }" :class="{ 'faint': shouldBeDisabled }"
@ -20,7 +21,9 @@
<input <input
:id="path" :id="path"
class="input string-input" class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled" :disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions"
:value="realDraftMode ? draft : state" :value="realDraftMode ? draft : state"
@change="update" @change="update"
> >
@ -30,7 +33,7 @@
:onclick="reset" :onclick="reset"
/> />
<ProfileSettingIndicator :is-profile="isProfileSetting" /> <ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons /> <DraftButtons v-if="!hideDraftButtons" />
<p <p
v-if="backendDescriptionDescription" v-if="backendDescriptionDescription"
class="setting-description" class="setting-description"

View file

@ -0,0 +1,16 @@
import Setting from './setting.js'
export default {
...Setting,
methods: {
...Setting.methods,
getValue ({ e, side }) {
const [a, b] = this.visibleState || []
if (side === 0) {
return { tuple: [e.target.value, b]}
} else {
return { tuple: [a, e.target.value]}
}
}
}
}

View file

@ -0,0 +1,67 @@
<template>
<label
v-if="matchesExpertLevel"
class="TupleSetting"
>
<label
v-if="!hideLabel"
:for="path"
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions?.[0]?.[0]"
:value="visibleState?.tuple?.[0]"
@change="e => update({ e, side: 0 })"
>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions?.[0]?.[1]"
:value="visibleState?.tuple?.[1]"
@change="e => update({ e, side: 1 })"
>
{{ ' ' }}
<input
:id="path"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
:placeholder="backendDescriptionSuggestions?.[0]?.[1]"
:value="visibleState?.tuple?.[2]"
@change="e => update({ e, side: 2 })"
>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons v-if="!hideDraftButtons" />
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
</label>
</template>
<script src="./tuple_setting.js"></script>

View file

@ -159,10 +159,10 @@ export default {
const wrapperClasses = ['tab-content-wrapper', active ? '-active' : '-hidden' ] const wrapperClasses = ['tab-content-wrapper', active ? '-active' : '-hidden' ]
const contentClasses = ['tab-content'] const contentClasses = ['tab-content']
if (props['full-width']) { if (props['full-width'] || props['full-width'] === '') {
contentClasses.push('-full-width') contentClasses.push('-full-width')
} }
if (props['full-height']) { if (props['full-height'] || props['full-width'] === '') {
contentClasses.push('-full-height') contentClasses.push('-full-height')
} }
return ( return (

View file

@ -31,6 +31,14 @@
margin-top: 0; margin-top: 0;
} }
p {
line-height: 1.5;
}
.setting-item > p {
margin-left: 1em;
}
.setting-list, .setting-list,
.option-list { .option-list {
list-style-type: none; list-style-type: none;
@ -41,8 +49,21 @@
padding: 0 2em; padding: 0 2em;
} }
.btn-group {
.button-default {
flex: 0 1 auto;
}
}
li { li {
margin: 1em 0; display: block;
margin: 0.75em 0;
> label {
display: block;
margin: 0.75em 0;
padding: 0.5em 0;
}
} }
.suboptions { .suboptions {
@ -62,8 +83,8 @@
.setting-description { .setting-description {
margin-top: 0.2em; margin-top: 0.2em;
margin-bottom: 2em; margin-bottom: 0;
font-size: 70%; font-size: 80%;
} }
.settings-modal-panel { .settings-modal-panel {

View file

@ -1,32 +1,58 @@
import VerticalTabSwitcher from './helpers/vertical_tab_switcher.jsx' import VerticalTabSwitcher from './helpers/vertical_tab_switcher.jsx'
import InstanceTab from './admin_tabs/instance_tab.vue' import InstanceTab from './admin_tabs/instance_tab.vue'
import LinksTab from './admin_tabs/links_tab.vue'
import LimitsTab from './admin_tabs/limits_tab.vue' import LimitsTab from './admin_tabs/limits_tab.vue'
import FrontendsTab from './admin_tabs/frontends_tab.vue' import FrontendsTab from './admin_tabs/frontends_tab.vue'
import MediaProxyTab from './admin_tabs/media_proxy_tab.vue'
import EmojiTab from './admin_tabs/emoji_tab.vue' import EmojiTab from './admin_tabs/emoji_tab.vue'
import UploadsTab from './admin_tabs/uploads_tab.vue'
import MailerTab from './admin_tabs/mailer_tab.vue'
import MonitoringTab from './admin_tabs/monitoring_tab.vue'
import RegistrationsTab from './admin_tabs/registrations_tab.vue'
import AuthTab from './admin_tabs/auth_tab.vue'
import HTTPTab from './admin_tabs/http_tab.vue'
import OtherTab from './admin_tabs/other_tab.vue'
import PostsTab from './admin_tabs/posts_tab.vue'
import FederationTab from './admin_tabs/federation_tab.vue'
import JobQueuesTab from './admin_tabs/job_queues_tab.vue'
import { useInterfaceStore } from 'src/stores/interface' import { useInterfaceStore } from 'src/stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
faWrench, faWrench,
faHand, faHand,
faChain,
faGlobe,
faLaptopCode, faLaptopCode,
faPaintBrush, faTowerBroadcast,
faBell, faEnvelope,
faDownload, faChartLine,
faEyeSlash, faDoorOpen,
faInfo faGears,
faKey,
faCircleNodes,
faUpload,
faMessage,
faEllipsis
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
library.add( library.add(
faWrench, faWrench,
faHand, faHand,
faChain,
faGlobe,
faLaptopCode, faLaptopCode,
faPaintBrush, faTowerBroadcast,
faBell, faEnvelope,
faDownload, faChartLine,
faEyeSlash, faDoorOpen,
faInfo faGears,
faKey,
faCircleNodes,
faUpload,
faMessage,
faEllipsis
) )
const SettingsModalAdminContent = { const SettingsModalAdminContent = {
@ -34,9 +60,21 @@ const SettingsModalAdminContent = {
VerticalTabSwitcher, VerticalTabSwitcher,
InstanceTab, InstanceTab,
LimitsTab, RegistrationsTab,
EmojiTab,
FrontendsTab, FrontendsTab,
EmojiTab FederationTab,
LimitsTab,
MailerTab,
UploadsTab,
MediaProxyTab,
LinksTab,
JobQueuesTab,
AuthTab,
HTTPTab,
MonitoringTab,
OtherTab,
PostsTab
}, },
computed: { computed: {
user () { user () {

View file

@ -48,6 +48,41 @@
> >
<InstanceTab /> <InstanceTab />
</div> </div>
<div
:label="$t('admin_dash.tabs.registrations')"
icon="door-open"
data-tab-name="registrations"
>
<RegistrationsTab />
</div>
<div
:label="$t('admin_dash.tabs.auth')"
icon="key"
data-tab-name="monitoring"
>
<AuthTab />
</div>
<div
:label="$t('admin_dash.tabs.emoji')"
icon="face-smile-beam"
data-tab-name="emoji"
full-width
>
<EmojiTab />
</div>
<div
:label="$t('admin_dash.tabs.frontends')"
icon="laptop-code"
data-tab-name="frontends"
full-width
>
<FrontendsTab />
</div>
<div <div
v-if="adminDbLoaded" v-if="adminDbLoaded"
:label="$t('admin_dash.tabs.limits')" :label="$t('admin_dash.tabs.limits')"
@ -56,20 +91,85 @@
> >
<LimitsTab /> <LimitsTab />
</div> </div>
<div <div
:label="$t('admin_dash.tabs.frontends')" :label="$t('admin_dash.tabs.uploads')"
icon="laptop-code" icon="upload"
data-tab-name="frontends" data-tab-name="uploads"
> >
<FrontendsTab /> <UploadsTab />
</div> </div>
<div <div
:label="$t('admin_dash.tabs.emoji')" :label="$t('admin_dash.tabs.media_proxy')"
icon="face-smile-beam" icon="tower-broadcast"
data-tab-name="emoji" data-tab-name="media_proxy"
> >
<EmojiTab /> <MediaProxyTab />
</div>
<div
:label="$t('admin_dash.tabs.posts')"
icon="message"
data-tab-name="other"
>
<PostsTab />
</div>
<div
:label="$t('admin_dash.tabs.links')"
icon="chain"
data-tab-name="links"
>
<LinksTab />
</div>
<div
:label="$t('admin_dash.tabs.mailer')"
icon="envelope"
data-tab-name="mailer"
>
<MailerTab />
</div>
<div
:label="$t('admin_dash.tabs.federation')"
icon="circle-nodes"
data-tab-name="monitoring"
>
<FederationTab />
</div>
<div
:label="$t('admin_dash.tabs.http')"
icon="globe"
data-tab-name="http"
>
<HTTPTab />
</div>
<div
:label="$t('admin_dash.tabs.job_queues')"
icon="gears"
data-tab-name="job_queues"
>
<JobQueuesTab />
</div>
<div
:label="$t('admin_dash.tabs.monitoring')"
icon="chart-line"
data-tab-name="monitoring"
>
<MonitoringTab />
</div>
<div
:label="$t('admin_dash.tabs.other')"
icon="ellipsis"
data-tab-name="other"
>
<OtherTab />
</div> </div>
</vertical-tab-switcher> </vertical-tab-switcher>
</template> </template>

View file

@ -161,13 +161,16 @@
<div class="fun-monitor-neck button-default" /> <div class="fun-monitor-neck button-default" />
<div class="fun-monitor-display-bezel button-default"> <div class="fun-monitor-display-bezel button-default">
<div class="fun-monitor-display-screen input"> <div class="fun-monitor-display-screen input">
<img <img
v-if="backgroundPreview || user.background_image || instanceWallpaper" v-if="backgroundPreview || user.background_image || instanceWallpaper"
class="fun-monitor-display-screen-image" class="fun-monitor-display-screen-image"
:src="backgroundPreview || user.background_image || instanceWallpaper" :src="backgroundPreview || user.background_image || instanceWallpaper"
/> >
<div v-else class="wallpaper" /> <div
<div class="fun-monitor-display-screen-overlay input" /> v-else
class="wallpaper"
/>
<div class="fun-monitor-display-screen-overlay input" />
<div <div
v-if="backgroundUploading" v-if="backgroundUploading"
class="fun-monitor-display-uploading" class="fun-monitor-display-uploading"

View file

@ -217,7 +217,6 @@ const FilteringTab = {
}, },
purgeExpiredFilters () { purgeExpiredFilters () {
this.muteFiltersExpired.forEach(([id]) => { this.muteFiltersExpired.forEach(([id]) => {
console.log(id)
delete this.muteFiltersDraftObject[id] delete this.muteFiltersDraftObject[id]
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null }) this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
}) })

View file

@ -0,0 +1,26 @@
<template>
<div :label="$t('admin_dash.tabs.uploads')">
<div class="setting-item">
<h3>{{ $t('admin_dash.uploads.general') }}</h3>
<ul class="setting-list">
<li>
<ChoiceSetting
:path="[':pleroma','Pleroma.Upload']"
:option-label-map="adaptersLabels"
/>
</li>
<h4>{{ $t('admin_dash.mailer.auth') }}</h4>
<li>
<!-- authentication info -->
<StringSetting
:path="[':pleroma','Pleroma.Emails.Mailer',':api_key']"
:password="true"
:subgroup="adapter"
/>
</li>
</ul>
</div>
</div>
</template>
<script src="./uploads_tab.js"></script>

View file

@ -1152,7 +1152,80 @@
"instance": "Instance", "instance": "Instance",
"limits": "Limits", "limits": "Limits",
"frontends": "Front-ends", "frontends": "Front-ends",
"emoji": "Emoji" "mailer": "EMails",
"media_proxy": "Media Proxy",
"emoji": "Emoji",
"uploads": "Uploads",
"monitoring": "Monitoring",
"registrations": "Registrations",
"links": "Links",
"job_queues": "Job Queues",
"auth": "Auth",
"posts": "Posts",
"http": "HTTP",
"federation": "Federation",
"other": "Other"
},
"posts": {
"global": "Global settings",
"local": "Local posts",
"scheduled_activites": "Scheduled posts",
"remote": "Remote posts"
},
"other": {
"uncategorized": "Uncategorized",
"user_backup": "User Backup",
"reports": "Reports",
"feed": "RSS Feed",
"feed_title": "Article Title",
"mime": "MIME Types",
"remote_ip": "Reverse Proxy / Remote IP",
"remote_ip_description": "This should be disabled only if your instance is NOT behind a reverse proxy",
"streamer": "Notifications Streamer",
"privileges": "Privileges"
},
"monitoring": {
"builtins": "Built-in Tools",
"prometheus": "Prometheus Exporter"
},
"federation": {
"global": "Global settings",
"restrictions": "Restrictions",
"activitypub": "ActivityPub"
},
"http": {
"outbound": "Outbound HTTP requests",
"socks5": "SOCKS5"
},
"auth": {
"MFA": "Multi-factor Authentication",
"LDAP": "LDAP Settings",
"OAuth": "Oauth2 settings",
"TOTP": "One-time Passwords (TOTP)",
"backup_codes": "Backup codes"
},
"job_queues": {
"Oban": "Oban queues",
"Gun": {
"title": "Gun queues",
"connections_pools": "Gun connections pool",
"pools": {
"title": "Gun worker pools",
"default": "Default pool",
"federation": "Federation pool",
"media": "Media pool",
"rich_media": "Rich media pool",
"upload": "Upload pool"
}
},
"Hackney": {
"title": "Hackney pools",
"federation": "Federation",
"media": "Media",
"rich_media": "Rich media",
"upload": "Upload"
},
"queues": "Queues"
}, },
"nodb": { "nodb": {
"heading": "Database config is disabled", "heading": "Database config is disabled",
@ -1164,29 +1237,79 @@
"native": "Native", "native": "Native",
"kocaptcha": "KoCaptcha" "kocaptcha": "KoCaptcha"
}, },
"http": {
"outbound": "Outgoing connections",
"incoming": "Incoming connections",
"security": "HTTP Security",
"cors": "Cross-origin Resource Sharing (CORS)",
"web_cache_ttl": "Web Cache TTL",
"web_cache_ttl_description": "Amount of milliseconds until web response cache is cleared. Use `nil` to disable expiration entirely.",
"web_push": "Web Push",
"web_push_description": "Web Push VAPID settings. You can use the mix task web_push.gen.keypair to generate it."
},
"instance": { "instance": {
"instance": "Instance information", "instance": "Instance information",
"branding": "Branding",
"registrations": "User sign-ups", "registrations": "User sign-ups",
"captcha_header": "CAPTCHA", "captcha_header": "CAPTCHA",
"kocaptcha": "KoCaptcha settings", "kocaptcha": "KoCaptcha settings",
"access": "Instance access", "access": "Instance access",
"rich_metadata": "Metadata",
"restrict": { "restrict": {
"header": "Restrict access for anonymous visitors", "header": "Restrict access for anonymous visitors",
"description": "Detailed setting for allowing/disallowing access to certain aspects of API. By default (indeterminate state) it will disallow if instance is not public, ticked checkbox means disallow access even if instance is public, unticked means allow access even if instance is private. Please note that unexpected behavior might happen if some settings are set, i.e. if profile access is disabled posts will show without profile information.", "description": "Detailed setting for allowing/disallowing access to certain aspects of API. By default (indeterminate state) it will disallow if instance is not public, ticked checkbox means disallow access even if instance is public, unticked means allow access even if instance is private. Please note that unexpected behavior might happen if some settings are set, i.e. if profile access is disabled posts will show without profile information.",
"timelines": "Timelines access", "timelines": "Timelines access",
"profiles": "User profiles access", "profiles": "User profiles access",
"activities": "Statuses/activities access" "activities": "Statuses/activities access"
} },
":unauthenticated": "Unauthenticated",
":all": "Everyone"
},
"registrations": {
"welcome": {
"title": "Welcome message",
"description": "Send new users a message when they sign up",
"direct_message": "Via direct message",
"chat_message": "Via chat",
"email_message": "Via email"
},
"restrictions": "Restrictions",
"autofollow": "Autofollow"
},
"links": {
"no_scheme": "No scheme",
"link_previews": "Link previews",
"link_formatter": "Link formatter"
},
"uploads": {
"attachments": "Attachments settings",
"upload": "Upload",
"local_uploader": "Local files",
"filenames": "Filenames, Titles and Descriptions",
"uploader_settings": "Uploader settings"
},
"media_proxy": {
"basic": "Basic Settings",
"invalidation": "Cache Invalidation",
"limits": "Limits",
"thumbnails": "Thumbnail Generation",
"invalidation_settings": "Cache Invalidation"
},
"mailer": {
"adapter": "Mailing Adapter",
"auth": "Authentication"
}, },
"limits": { "limits": {
"arbitrary_limits": "Arbitrary limits", "arbitrary_limits": "Arbitrary limits",
"posts": "Post limits", "posts": "Post limits",
"other": "Misc. limits",
"uploads": "Attachments limits", "uploads": "Attachments limits",
"users": "User profile limits", "users": "User profile limits",
"profile_fields": "Profile fields limits", "profile_fields": "Profile fields limits",
"user_uploads": "Profile media limits" "user_uploads": "Profile media limits"
}, },
"frontend": { "frontend": {
"title": "Frontend management",
"repository": "Repository link", "repository": "Repository link",
"versions": "Available versions", "versions": "Available versions",
"build_url": "Build URL", "build_url": "Build URL",
@ -1210,7 +1333,11 @@
"emoji": { "emoji": {
"global_actions": "Global actions", "global_actions": "Global actions",
"reload": "Reload emoji", "reload": "Reload emoji",
"reload_short": "Refresh",
"advanced": "Advanced",
"importFS": "Import emoji from filesystem", "importFS": "Import emoji from filesystem",
"import_pack": "Upload emoji pack",
"import_pack_short": "Import",
"error": "Error: {0}", "error": "Error: {0}",
"create_pack": "Create pack", "create_pack": "Create pack",
"delete_pack": "Delete pack", "delete_pack": "Delete pack",
@ -1218,10 +1345,12 @@
"create": "Create", "create": "Create",
"emoji_packs": "Emoji packs", "emoji_packs": "Emoji packs",
"remote_packs": "Remote packs", "remote_packs": "Remote packs",
"remote_packs_short": "Remote",
"do_list": "List", "do_list": "List",
"remote_pack_instance": "Remote pack instance", "remote_pack_instance": "Remote pack instance",
"emoji_pack": "Emoji pack", "emoji_pack": "Emoji pack",
"edit_pack": "Edit pack", "edit_pack": "Edit pack",
"metadata": "Metadata",
"description": "Description", "description": "Description",
"homepage": "Homepage", "homepage": "Homepage",
"fallback_src": "Fallback source", "fallback_src": "Fallback source",
@ -1257,8 +1386,67 @@
"replace_warning": "This will REPLACE the local pack of the same name", "replace_warning": "This will REPLACE the local pack of the same name",
"copied_successfully": "Successfully copied emoji \"{0}\" to pack \"{1}\"" "copied_successfully": "Successfully copied emoji \"{0}\" to pack \"{1}\""
}, },
"generic_enforcement": {
"if_available": "If available",
"always": "Always",
"never": "Never"
},
"temp_overrides": { "temp_overrides": {
":pleroma": { ":pleroma": {
"Oban": {
":queues": {
":scheduled_activites": {
"label": "Scheduled activities",
"description": "Scheduled activites queue"
},
":slow": {
"label": "Slow",
"description": "idk"
}
}
},
":connections_pool": {
":max_idle_time": {
"label": "Maximum idle time",
"description": "idk"
},
":retry": {
"label": "Retry",
"description": "idk"
}
},
":pools": {
":rich_media": {
"label": "Rich media",
"description": "idk"
}
},
"Pleroma_DOT_Formatter": {
":attribute_toggle": {
"label": "Set {attr} attribute"
},
":truncate_toggle": {
"label": "Truncate"
},
":class": {
"label": "Value"
},
":rel": {
"label": "Value"
}
},
"Pleroma_DOT_Uploaders_DOT_Uploader": {
":timeout": {
"label": "Timeout",
"description": "Amount of milliseconds before dropping connection"
}
},
"Pleroma_DOT_Upload": {
":default_description": {
"label": "Default description",
"description": "Default description to give to a file. Setting it to ':filename' will use file's filename as description."
}
},
":instance": { ":instance": {
":public": { ":public": {
"label": "Instance is public", "label": "Instance is public",
@ -1276,6 +1464,16 @@
"label": "Background image", "label": "Background image",
"description": "Background image (primarily used by PleromaFE)" "description": "Background image (primarily used by PleromaFE)"
} }
},
":http_security": {
":allow_unsafe_eval": {
"label": "Allow unsafe-eval",
"description": "Allow unsafe evaluation of scripts (required for Flash support)"
},
":report_url": {
"label": "Report URL",
"description": "URL to report security violations to"
}
} }
} }
} }

View file

@ -105,6 +105,16 @@ const adminSettingsStorage = {
} }
set(config, path, convert(c.value)) set(config, path, convert(c.value))
}) })
console.log('CONFIG', JSON.parse(JSON.stringify(config)))
// patching http adapter config to be easier to handle
const adapter = config[':pleroma'][':http'][':adapter']
if (Array.isArray(adapter)) {
config[':pleroma'][':http'][':adapter'] = {
[':ssl_options']: {
[':versions']: []
}
}
}
commit('updateAdminSettings', { config, modifiedPaths }) commit('updateAdminSettings', { config, modifiedPaths })
commit('resetAdminDraft') commit('resetAdminDraft')
}, },
@ -121,7 +131,9 @@ const adminSettingsStorage = {
} }
const descriptions = {} const descriptions = {}
backendDescriptions.forEach(d => convert(d, '', descriptions)) backendDescriptions.forEach(d => convert(d, '', descriptions))
console.log('DESCRIPTIONS', descriptions)
commit('updateAdminDescriptions', { descriptions }) commit('updateAdminDescriptions', { descriptions })
}, },
@ -206,7 +218,7 @@ const adminSettingsStorage = {
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig })) .then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
}, },
resetAdminSetting ({ rootState, state, dispatch }, { path }) { resetAdminSetting ({ rootState, state, dispatch }, { path }) {
const [group, key, subkey] = path.split(/\./g) const [group, key, subkey] = Array.isArray(path) ? path : path.split(/\./g)
state.modifiedPaths.delete(path) state.modifiedPaths.delete(path)